summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/fpdfapi/page/cpdf_pageobjectholder.cpp5
-rw-r--r--core/fpdfapi/page/cpdf_pageobjectholder.h11
-rw-r--r--core/fpdfapi/page/cpdf_streamcontentparser.cpp10
-rw-r--r--core/fpdfapi/render/cpdf_progressiverenderer.cpp6
-rw-r--r--core/fpdfapi/render/cpdf_renderoptions.h1
-rw-r--r--fpdfsdk/fpdfview.cpp207
6 files changed, 223 insertions, 17 deletions
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.cpp b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
index 974baa65a0..5f5597e5ac 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.cpp
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
@@ -21,7 +21,6 @@ CPDF_PageObjectHolder::CPDF_PageObjectHolder()
m_pResources(nullptr),
m_Transparency(0),
m_bBackgroundAlphaNeeded(false),
- m_bHasImageMask(false),
m_ParseState(CONTENT_NOT_PARSED) {}
CPDF_PageObjectHolder::~CPDF_PageObjectHolder() {}
@@ -37,6 +36,10 @@ void CPDF_PageObjectHolder::ContinueParse(IFX_Pause* pPause) {
}
}
+void CPDF_PageObjectHolder::AddImageMaskBoundingBox(const CFX_FloatRect& box) {
+ m_MaskBoundingBoxes.push_back(box);
+}
+
void CPDF_PageObjectHolder::Transform(const CFX_Matrix& matrix) {
for (auto& pObj : m_PageObjectList)
pObj->Transform(matrix);
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.h b/core/fpdfapi/page/cpdf_pageobjectholder.h
index aee7617c1f..0b88a86518 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.h
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.h
@@ -8,6 +8,7 @@
#define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_
#include <memory>
+#include <vector>
#include "core/fpdfapi/page/cpdf_pageobjectlist.h"
#include "core/fxcrt/fx_coordinates.h"
@@ -41,9 +42,11 @@ class CPDF_PageObjectHolder {
m_bBackgroundAlphaNeeded = needed;
}
- bool HasImageMask() const { return m_bHasImageMask; }
- void SetHasImageMask(bool value) { m_bHasImageMask = value; }
-
+ bool HasImageMask() const { return !m_MaskBoundingBoxes.empty(); }
+ const std::vector<CFX_FloatRect>& GetMaskBoundingBoxes() const {
+ return m_MaskBoundingBoxes;
+ }
+ void AddImageMaskBoundingBox(const CFX_FloatRect& box);
void Transform(const CFX_Matrix& matrix);
CFX_FloatRect CalcBoundingBox() const;
@@ -61,7 +64,7 @@ class CPDF_PageObjectHolder {
void LoadTransInfo();
bool m_bBackgroundAlphaNeeded;
- bool m_bHasImageMask;
+ std::vector<CFX_FloatRect> m_MaskBoundingBoxes;
ParseState m_ParseState;
std::unique_ptr<CPDF_ContentParser> m_pParser;
CPDF_PageObjectList m_PageObjectList;
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
index 30686133c2..d9f12f121b 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -735,7 +735,11 @@ void CPDF_StreamContentParser::Handle_ExecuteXObject() {
CFX_ByteString name = GetString(0);
if (name == m_LastImageName && m_pLastImage && m_pLastImage->GetStream() &&
m_pLastImage->GetStream()->GetObjNum()) {
- AddImage(m_pLastImage);
+ CPDF_ImageObject* pObj = AddImage(m_pLastImage);
+ // Record the bounding box of this image, so rendering code can draw it
+ // properly.
+ if (pObj->GetImage()->IsMask())
+ m_pObjectHolder->AddImageMaskBoundingBox(pObj->GetRect());
return;
}
@@ -757,8 +761,8 @@ void CPDF_StreamContentParser::Handle_ExecuteXObject() {
m_LastImageName = name;
m_pLastImage = pObj->GetImage();
- if (!m_pObjectHolder->HasImageMask())
- m_pObjectHolder->SetHasImageMask(m_pLastImage->IsMask());
+ if (m_pLastImage->IsMask())
+ m_pObjectHolder->AddImageMaskBoundingBox(pObj->GetRect());
} else if (type == "Form") {
AddForm(pXObject);
}
diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.cpp b/core/fpdfapi/render/cpdf_progressiverenderer.cpp
index 6aa3b8a309..c3cef1fc77 100644
--- a/core/fpdfapi/render/cpdf_progressiverenderer.cpp
+++ b/core/fpdfapi/render/cpdf_progressiverenderer.cpp
@@ -6,6 +6,8 @@
#include "core/fpdfapi/render/cpdf_progressiverenderer.h"
+#include "core/fpdfapi/page/cpdf_image.h"
+#include "core/fpdfapi/page/cpdf_imageobject.h"
#include "core/fpdfapi/page/cpdf_pageobject.h"
#include "core/fpdfapi/page/cpdf_pageobjectholder.h"
#include "core/fpdfapi/render/cpdf_pagerendercache.h"
@@ -95,6 +97,10 @@ void CPDF_ProgressiveRenderer::Continue(IFX_Pause* pPause) {
return;
nObjsToGo = kStepLimit;
}
+ if (pCurObj->IsImage() && pCurObj->AsImage()->GetImage()->IsMask() &&
+ (m_pOptions->m_Flags & RENDER_BREAKFORMASKS)) {
+ return;
+ }
++iter;
}
if (m_pCurrentLayer->m_pObjectHolder->IsParsed()) {
diff --git a/core/fpdfapi/render/cpdf_renderoptions.h b/core/fpdfapi/render/cpdf_renderoptions.h
index b934941d00..ee26ccbb07 100644
--- a/core/fpdfapi/render/cpdf_renderoptions.h
+++ b/core/fpdfapi/render/cpdf_renderoptions.h
@@ -28,6 +28,7 @@
#define RENDER_PRINTIMAGETEXT 0x00000200
#define RENDER_OVERPRINT 0x00000400
#define RENDER_THINLINE 0x00000800
+#define RENDER_BREAKFORMASKS 0x00001000
#define RENDER_NOTEXTSMOOTH 0x10000000
#define RENDER_NOPATHSMOOTH 0x20000000
#define RENDER_NOIMAGESMOOTH 0x40000000
diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp
index 5010c17ceb..6f32473985 100644
--- a/fpdfsdk/fpdfview.cpp
+++ b/fpdfsdk/fpdfview.cpp
@@ -8,10 +8,14 @@
#include <memory>
#include <utility>
+#include <vector>
#include "core/fpdfapi/cpdf_modulemgr.h"
#include "core/fpdfapi/cpdf_pagerendercontext.h"
+#include "core/fpdfapi/page/cpdf_image.h"
+#include "core/fpdfapi/page/cpdf_imageobject.h"
#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pageobject.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/fpdf_parser_decode.h"
@@ -687,6 +691,146 @@ DLLEXPORT double STDCALL FPDF_GetPageHeight(FPDF_PAGE page) {
}
#if defined(_WIN32)
+namespace {
+
+const double kEpsilonSize = 0.01f;
+
+void GetScaling(CPDF_Page* pPage,
+ int size_x,
+ int size_y,
+ int rotate,
+ double* scale_x,
+ double* scale_y) {
+ ASSERT(pPage);
+ ASSERT(scale_x);
+ ASSERT(scale_y);
+ double page_width = pPage->GetPageWidth();
+ double page_height = pPage->GetPageHeight();
+ if (page_width < kEpsilonSize || page_height < kEpsilonSize)
+ return;
+
+ if (rotate % 2 == 0) {
+ *scale_x = size_x / page_width;
+ *scale_y = size_y / page_height;
+ } else {
+ *scale_x = size_y / page_width;
+ *scale_y = size_x / page_height;
+ }
+}
+
+FX_RECT GetMaskDimensionsAndOffsets(CPDF_Page* pPage,
+ int start_x,
+ int start_y,
+ int size_x,
+ int size_y,
+ int rotate,
+ const CFX_FloatRect& mask_box) {
+ double scale_x = 0.0f;
+ double scale_y = 0.0f;
+ GetScaling(pPage, size_x, size_y, rotate, &scale_x, &scale_y);
+ if (scale_x < kEpsilonSize || scale_y < kEpsilonSize)
+ return FX_RECT();
+
+ // Compute sizes in page points. Round down to catch the entire bitmap.
+ int start_x_bm = static_cast<int>(mask_box.left * scale_x);
+ int start_y_bm = static_cast<int>(mask_box.bottom * scale_y);
+ int size_x_bm = static_cast<int>(mask_box.right * scale_x + 1.0f) -
+ static_cast<int>(mask_box.left * scale_x);
+ int size_y_bm = static_cast<int>(mask_box.top * scale_y + 1.0f) -
+ static_cast<int>(mask_box.bottom * scale_y);
+
+ // Get page rotation
+ int page_rotation = 0;
+ CPDF_Dictionary* pDict = pPage->m_pFormDict;
+ while (pDict) {
+ if (pDict->KeyExist("Rotate")) {
+ CPDF_Object* pRotateObj = pDict->GetObjectFor("Rotate")->GetDirect();
+ page_rotation = pRotateObj ? pRotateObj->GetInteger() / 90 : 0;
+ break;
+ }
+ if (!pDict->KeyExist("Parent"))
+ break;
+
+ pDict = ToDictionary(pDict->GetObjectFor("Parent")->GetDirect());
+ }
+
+ // Compute offsets
+ int offset_x = 0;
+ int offset_y = 0;
+ switch ((rotate + page_rotation) % 4) {
+ case 0:
+ offset_x = start_x_bm + start_x;
+ offset_y = start_y + size_y - size_y_bm - start_y_bm;
+ break;
+ case 1:
+ offset_x = start_y_bm + start_x;
+ offset_y = start_x_bm + start_y;
+ break;
+ case 2:
+ offset_x = start_x + size_x - size_x_bm - start_x_bm;
+ offset_y = start_y_bm + start_y;
+ break;
+ case 3:
+ offset_x = start_x + size_x - size_x_bm - start_y_bm;
+ offset_y = start_y + size_y - size_y_bm - start_x_bm;
+ break;
+ }
+ return FX_RECT(offset_x, offset_y, offset_x + size_x_bm,
+ offset_y + size_y_bm);
+}
+
+// Get a bitmap of just the mask section defined by |mask_box| from a full page
+// bitmap |pBitmap|.
+CFX_RetainPtr<CFX_DIBitmap> GetMaskBitmap(CPDF_Page* pPage,
+ int start_x,
+ int start_y,
+ int size_x,
+ int size_y,
+ int rotate,
+ CFX_RetainPtr<CFX_DIBitmap>& pSrc,
+ const CFX_FloatRect& mask_box,
+ FX_RECT* bitmap_area) {
+ ASSERT(bitmap_area);
+ *bitmap_area = GetMaskDimensionsAndOffsets(pPage, start_x, start_y, size_x,
+ size_y, rotate, mask_box);
+ if (bitmap_area->IsEmpty())
+ return nullptr;
+
+ // Create a new bitmap to transfer part of the page bitmap to.
+ CFX_RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
+ pDst->Create(bitmap_area->Width(), bitmap_area->Height(), FXDIB_Argb);
+ pDst->Clear(0x00ffffff);
+ pDst->TransferBitmap(0, 0, bitmap_area->Width(), bitmap_area->Height(), pSrc,
+ bitmap_area->left, bitmap_area->top);
+ return pDst;
+}
+
+void RenderBitmap(CFX_RenderDevice* device,
+ const CFX_RetainPtr<CFX_DIBitmap>& pSrc,
+ const FX_RECT& mask_area) {
+ int size_x_bm = mask_area.Width();
+ int size_y_bm = mask_area.Height();
+ if (size_x_bm == 0 || size_y_bm == 0)
+ return;
+
+ // Create a new bitmap from the old one
+ CFX_RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
+ pDst->Create(size_x_bm, size_y_bm, FXDIB_Rgb32);
+ pDst->Clear(0xffffffff);
+ pDst->CompositeBitmap(0, 0, size_x_bm, size_y_bm, pSrc, 0, 0,
+ FXDIB_BLEND_NORMAL, nullptr, false);
+
+ if (device->GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) {
+ device->StretchDIBitsWithFlagsAndBlend(pDst, mask_area.left, mask_area.top,
+ size_x_bm, size_y_bm, 0,
+ FXDIB_BLEND_NORMAL);
+ } else {
+ device->SetDIBits(pDst, mask_area.left, mask_area.top);
+ }
+}
+
+} // namespace
+
DLLEXPORT void STDCALL FPDF_RenderPage(HDC dc,
FPDF_PAGE page,
int start_x,
@@ -698,30 +842,74 @@ DLLEXPORT void STDCALL FPDF_RenderPage(HDC dc,
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
if (!pPage)
return;
-
- CPDF_PageRenderContext* pContext = new CPDF_PageRenderContext;
- pPage->SetRenderContext(pdfium::WrapUnique(pContext));
+ pPage->SetRenderContext(pdfium::MakeUnique<CPDF_PageRenderContext>());
+ CPDF_PageRenderContext* pContext = pPage->GetRenderContext();
CFX_RetainPtr<CFX_DIBitmap> pBitmap;
- // TODO: This results in unnecessary rasterization of some PDFs due to
- // HasImageMask() returning true. If any image on the page is a mask, the
- // entire page gets rasterized and the spool size gets huge.
+ // Don't render the full page to bitmap for a mask unless there are a lot
+ // of masks. Full page bitmaps result in large spool sizes, so they should
+ // only be used when necessary. For large numbers of masks, rendering each
+ // individually is inefficient and unlikely to significantly improve spool
+ // size.
const bool bNewBitmap =
- pPage->BackgroundAlphaNeeded() || pPage->HasImageMask();
- if (bNewBitmap) {
+ pPage->BackgroundAlphaNeeded() ||
+ (pPage->HasImageMask() && pPage->GetMaskBoundingBoxes().size() > 100);
+ const bool bHasMask = pPage->HasImageMask() && !bNewBitmap;
+ if (bNewBitmap || bHasMask) {
pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
pBitmap->Create(size_x, size_y, FXDIB_Argb);
pBitmap->Clear(0x00ffffff);
CFX_FxgeDevice* pDevice = new CFX_FxgeDevice;
pContext->m_pDevice = pdfium::WrapUnique(pDevice);
pDevice->Attach(pBitmap, false, nullptr, false);
+ if (bHasMask) {
+ pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
+ pContext->m_pOptions->m_Flags |= RENDER_BREAKFORMASKS;
+ }
} else {
pContext->m_pDevice = pdfium::MakeUnique<CFX_WindowsDevice>(dc);
}
+
FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y,
rotate, flags, true, nullptr);
- if (bNewBitmap) {
+ if (bHasMask) {
+ // Finish rendering the page to bitmap and copy the correct segments
+ // of the page to individual image mask bitmaps.
+ const std::vector<CFX_FloatRect>& mask_boxes =
+ pPage->GetMaskBoundingBoxes();
+ std::vector<FX_RECT> bitmap_areas(mask_boxes.size());
+ std::vector<CFX_RetainPtr<CFX_DIBitmap>> bitmaps(mask_boxes.size());
+ for (size_t i = 0; i < mask_boxes.size(); i++) {
+ bitmaps[i] =
+ GetMaskBitmap(pPage, start_x, start_y, size_x, size_y, rotate,
+ pBitmap, mask_boxes[i], &bitmap_areas[i]);
+ pContext->m_pRenderer->Continue(nullptr);
+ }
+
+ // Reset rendering context
+ pPage->SetRenderContext(nullptr);
+
+ // Begin rendering to the printer. Add flag to indicate the renderer should
+ // pause after each image mask.
+ pPage->SetRenderContext(pdfium::MakeUnique<CPDF_PageRenderContext>());
+ pContext = pPage->GetRenderContext();
+ pContext->m_pDevice = pdfium::MakeUnique<CFX_WindowsDevice>(dc);
+ pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
+ pContext->m_pOptions->m_Flags |= RENDER_BREAKFORMASKS;
+ FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y,
+ rotate, flags, true, nullptr);
+
+ // Render masks
+ for (size_t i = 0; i < mask_boxes.size(); i++) {
+ // Render the bitmap for the mask and free the bitmap.
+ if (bitmaps[i]) { // will be null if mask has zero area
+ RenderBitmap(pContext->m_pDevice.get(), bitmaps[i], bitmap_areas[i]);
+ }
+ // Render the next portion of page.
+ pContext->m_pRenderer->Continue(nullptr);
+ }
+ } else if (bNewBitmap) {
CFX_WindowsDevice WinDC(dc);
if (WinDC.GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) {
auto pDst = pdfium::MakeRetain<CFX_DIBitmap>();
@@ -735,6 +923,7 @@ DLLEXPORT void STDCALL FPDF_RenderPage(HDC dc,
WinDC.SetDIBits(pBitmap, 0, 0);
}
}
+
pPage->SetRenderContext(nullptr);
}
#endif // defined(_WIN32)