// Copyright 2014 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "xfa/fxfa/include/xfa_ffwidget.h" #include <algorithm> #include "core/fpdfapi/fpdf_page/include/cpdf_pageobjectholder.h" #include "core/fxcodec/codec/include/ccodec_progressivedecoder.h" #include "core/fxcodec/include/fx_codec.h" #include "xfa/fxfa/app/xfa_textlayout.h" #include "xfa/fxfa/include/xfa_ffapp.h" #include "xfa/fxfa/include/xfa_ffdoc.h" #include "xfa/fxfa/include/xfa_ffdocview.h" #include "xfa/fxfa/include/xfa_ffpageview.h" #include "xfa/fxfa/parser/cxfa_corner.h" #include "xfa/fxgraphics/cfx_color.h" #include "xfa/fxgraphics/cfx_path.h" #include "xfa/fxgraphics/cfx_pattern.h" #include "xfa/fxgraphics/cfx_shading.h" #include "xfa/fxgraphics/include/cfx_graphics.h" CXFA_FFWidget::CXFA_FFWidget(CXFA_FFPageView* pPageView, CXFA_WidgetAcc* pDataAcc) : CXFA_ContentLayoutItem(pDataAcc->GetNode()), m_pPageView(pPageView), m_pDataAcc(pDataAcc) { m_rtWidget.Set(0, 0, 0, 0); } CXFA_FFWidget::~CXFA_FFWidget() {} CXFA_FFPageView* CXFA_FFWidget::GetPageView() { return m_pPageView; } void CXFA_FFWidget::SetPageView(CXFA_FFPageView* pPageView) { m_pPageView = pPageView; } void CXFA_FFWidget::GetWidgetRect(CFX_RectF& rtWidget) { if ((m_dwStatus & XFA_WIDGETSTATUS_RectCached) == 0) { m_dwStatus |= XFA_WIDGETSTATUS_RectCached; GetRect(m_rtWidget); } rtWidget = m_rtWidget; } CFX_RectF CXFA_FFWidget::ReCacheWidgetRect() { m_dwStatus |= XFA_WIDGETSTATUS_RectCached; GetRect(m_rtWidget); return m_rtWidget; } void CXFA_FFWidget::GetRectWithoutRotate(CFX_RectF& rtWidget) { GetWidgetRect(rtWidget); FX_FLOAT fValue = 0; switch (m_pDataAcc->GetRotate()) { case 90: rtWidget.top = rtWidget.bottom(); fValue = rtWidget.width; rtWidget.width = rtWidget.height; rtWidget.height = fValue; break; case 180: rtWidget.left = rtWidget.right(); rtWidget.top = rtWidget.bottom(); break; case 270: rtWidget.left = rtWidget.right(); fValue = rtWidget.width; rtWidget.width = rtWidget.height; rtWidget.height = fValue; break; } } uint32_t CXFA_FFWidget::GetStatus() { return m_dwStatus; } void CXFA_FFWidget::ModifyStatus(uint32_t dwAdded, uint32_t dwRemoved) { m_dwStatus = (m_dwStatus & ~dwRemoved) | dwAdded; } FX_BOOL CXFA_FFWidget::GetBBox(CFX_RectF& rtBox, uint32_t dwStatus, FX_BOOL bDrawFocus) { if (bDrawFocus) return FALSE; if (m_pPageView) m_pPageView->GetPageViewRect(rtBox); return TRUE; } CXFA_WidgetAcc* CXFA_FFWidget::GetDataAcc() { return m_pDataAcc; } FX_BOOL CXFA_FFWidget::GetToolTip(CFX_WideString& wsToolTip) { if (CXFA_Assist assist = m_pDataAcc->GetAssist()) { if (CXFA_ToolTip toolTip = assist.GetToolTip()) { return toolTip.GetTip(wsToolTip); } } return GetCaptionText(wsToolTip); } void CXFA_FFWidget::RenderWidget(CFX_Graphics* pGS, CFX_Matrix* pMatrix, uint32_t dwStatus, int32_t iRotate) { if (!IsMatchVisibleStatus(dwStatus)) { return; } CXFA_Border border = m_pDataAcc->GetBorder(); if (border) { CFX_RectF rtBorder; GetRectWithoutRotate(rtBorder); CXFA_Margin margin = border.GetMargin(); if (margin) { XFA_RectWidthoutMargin(rtBorder, margin); } rtBorder.Normalize(); DrawBorder(pGS, border, rtBorder, pMatrix); } } FX_BOOL CXFA_FFWidget::IsLoaded() { return m_pPageView != NULL; } FX_BOOL CXFA_FFWidget::LoadWidget() { PerformLayout(); return TRUE; } void CXFA_FFWidget::UnloadWidget() {} FX_BOOL CXFA_FFWidget::PerformLayout() { ReCacheWidgetRect(); return TRUE; } FX_BOOL CXFA_FFWidget::UpdateFWLData() { return FALSE; } void CXFA_FFWidget::UpdateWidgetProperty() {} void CXFA_FFWidget::DrawBorder(CFX_Graphics* pGS, CXFA_Box box, const CFX_RectF& rtBorder, CFX_Matrix* pMatrix, uint32_t dwFlags) { XFA_DrawBox(box, pGS, rtBorder, pMatrix, dwFlags); } void CXFA_FFWidget::InvalidateWidget(const CFX_RectF* pRect) { if (!pRect) { CFX_RectF rtWidget; GetBBox(rtWidget, XFA_WIDGETSTATUS_Focused); rtWidget.Inflate(2, 2); GetDoc()->GetDocProvider()->InvalidateRect(m_pPageView, rtWidget, XFA_INVALIDATE_CurrentPage); } else { GetDoc()->GetDocProvider()->InvalidateRect(m_pPageView, *pRect, XFA_INVALIDATE_CurrentPage); } } void CXFA_FFWidget::AddInvalidateRect(const CFX_RectF* pRect) { CFX_RectF rtWidget; if (pRect) { rtWidget = *pRect; } else { GetBBox(rtWidget, XFA_WIDGETSTATUS_Focused); rtWidget.Inflate(2, 2); } m_pDocView->AddInvalidateRect(m_pPageView, rtWidget); } FX_BOOL CXFA_FFWidget::GetCaptionText(CFX_WideString& wsCap) { CXFA_TextLayout* pCapTextlayout = m_pDataAcc->GetCaptionTextLayout(); if (!pCapTextlayout) { return FALSE; } pCapTextlayout->GetText(wsCap); return TRUE; } FX_BOOL CXFA_FFWidget::IsFocused() { return m_dwStatus & XFA_WIDGETSTATUS_Focused; } FX_BOOL CXFA_FFWidget::OnMouseEnter() { return FALSE; } FX_BOOL CXFA_FFWidget::OnMouseExit() { return FALSE; } FX_BOOL CXFA_FFWidget::OnLButtonDown(uint32_t dwFlags, FX_FLOAT fx, FX_FLOAT fy) { return FALSE; } FX_BOOL CXFA_FFWidget::OnLButtonUp(uint32_t dwFlags, FX_FLOAT fx, FX_FLOAT fy) { return FALSE; } FX_BOOL CXFA_FFWidget::OnLButtonDblClk(uint32_t dwFlags, FX_FLOAT fx, FX_FLOAT fy) { return FALSE; } FX_BOOL CXFA_FFWidget::OnMouseMove(uint32_t dwFlags, FX_FLOAT fx, FX_FLOAT fy) { return FALSE; } FX_BOOL CXFA_FFWidget::OnMouseWheel(uint32_t dwFlags, int16_t zDelta, FX_FLOAT fx, FX_FLOAT fy) { return FALSE; } FX_BOOL CXFA_FFWidget::OnRButtonDown(uint32_t dwFlags, FX_FLOAT fx, FX_FLOAT fy) { return FALSE; } FX_BOOL CXFA_FFWidget::OnRButtonUp(uint32_t dwFlags, FX_FLOAT fx, FX_FLOAT fy) { return FALSE; } FX_BOOL CXFA_FFWidget::OnRButtonDblClk(uint32_t dwFlags, FX_FLOAT fx, FX_FLOAT fy) { return FALSE; } FX_BOOL CXFA_FFWidget::OnSetFocus(CXFA_FFWidget* pOldWidget) { CXFA_FFWidget* pParent = GetParent(); if (pParent && !pParent->IsAncestorOf(pOldWidget)) { pParent->OnSetFocus(pOldWidget); } m_dwStatus |= XFA_WIDGETSTATUS_Focused; CXFA_EventParam eParam; eParam.m_eType = XFA_EVENT_Enter; eParam.m_pTarget = m_pDataAcc; m_pDataAcc->ProcessEvent(XFA_ATTRIBUTEENUM_Enter, &eParam); return TRUE; } FX_BOOL CXFA_FFWidget::OnKillFocus(CXFA_FFWidget* pNewWidget) { m_dwStatus &= ~XFA_WIDGETSTATUS_Focused; EventKillFocus(); if (pNewWidget) { CXFA_FFWidget* pParent = GetParent(); if (pParent && !pParent->IsAncestorOf(pNewWidget)) { pParent->OnKillFocus(pNewWidget); } } return TRUE; } FX_BOOL CXFA_FFWidget::OnKeyDown(uint32_t dwKeyCode, uint32_t dwFlags) { return FALSE; } FX_BOOL CXFA_FFWidget::OnKeyUp(uint32_t dwKeyCode, uint32_t dwFlags) { return FALSE; } FX_BOOL CXFA_FFWidget::OnChar(uint32_t dwChar, uint32_t dwFlags) { return FALSE; } FWL_WidgetHit CXFA_FFWidget::OnHitTest(FX_FLOAT fx, FX_FLOAT fy) { return FWL_WidgetHit::Unknown; } FX_BOOL CXFA_FFWidget::OnSetCursor(FX_FLOAT fx, FX_FLOAT fy) { return FALSE; } void CXFA_FFWidget::Rotate2Normal(FX_FLOAT& fx, FX_FLOAT& fy) { CFX_Matrix mt; GetRotateMatrix(mt); if (mt.IsIdentity()) { return; } CFX_Matrix mtReverse; mtReverse.SetReverse(mt); mtReverse.TransformPoint(fx, fy); } static void XFA_GetMatrix(CFX_Matrix& m, int32_t iRotate, int32_t at, const CFX_RectF& rt) { if (!iRotate) { return; } FX_FLOAT fAnchorX, fAnchorY; switch (at) { case XFA_ATTRIBUTEENUM_TopLeft: fAnchorX = rt.left, fAnchorY = rt.top; break; case XFA_ATTRIBUTEENUM_TopCenter: fAnchorX = (rt.left + rt.right()) / 2, fAnchorY = rt.top; break; case XFA_ATTRIBUTEENUM_TopRight: fAnchorX = rt.right(), fAnchorY = rt.top; break; case XFA_ATTRIBUTEENUM_MiddleLeft: fAnchorX = rt.left, fAnchorY = (rt.top + rt.bottom()) / 2; break; case XFA_ATTRIBUTEENUM_MiddleCenter: fAnchorX = (rt.left + rt.right()) / 2, fAnchorY = (rt.top + rt.bottom()) / 2; break; case XFA_ATTRIBUTEENUM_MiddleRight: fAnchorX = rt.right(), fAnchorY = (rt.top + rt.bottom()) / 2; break; case XFA_ATTRIBUTEENUM_BottomLeft: fAnchorX = rt.left, fAnchorY = rt.bottom(); break; case XFA_ATTRIBUTEENUM_BottomCenter: fAnchorX = (rt.left + rt.right()) / 2, fAnchorY = rt.bottom(); break; case XFA_ATTRIBUTEENUM_BottomRight: fAnchorX = rt.right(), fAnchorY = rt.bottom(); break; } switch (iRotate) { case 90: m.a = 0, m.b = -1, m.c = 1, m.d = 0, m.e = fAnchorX - fAnchorY, m.f = fAnchorX + fAnchorY; break; case 180: m.a = -1, m.b = 0, m.c = 0, m.d = -1, m.e = fAnchorX * 2, m.f = fAnchorY * 2; break; case 270: m.a = 0, m.b = 1, m.c = -1, m.d = 0, m.e = fAnchorX + fAnchorY, m.f = fAnchorY - fAnchorX; break; } } void CXFA_FFWidget::GetRotateMatrix(CFX_Matrix& mt) { mt.Set(1, 0, 0, 1, 0, 0); int32_t iRotate = m_pDataAcc->GetRotate(); if (!iRotate) { return; } CFX_RectF rcWidget; GetRectWithoutRotate(rcWidget); XFA_ATTRIBUTEENUM at = XFA_ATTRIBUTEENUM_TopLeft; XFA_GetMatrix(mt, iRotate, at, rcWidget); } FX_BOOL CXFA_FFWidget::IsLayoutRectEmpty() { CFX_RectF rtLayout; GetRectWithoutRotate(rtLayout); return rtLayout.width < 0.1f && rtLayout.height < 0.1f; } CXFA_FFWidget* CXFA_FFWidget::GetParent() { CXFA_Node* pParentNode = m_pDataAcc->GetNode()->GetNodeItem(XFA_NODEITEM_Parent); if (pParentNode) { CXFA_WidgetAcc* pParentWidgetAcc = (CXFA_WidgetAcc*)pParentNode->GetWidgetData(); if (pParentWidgetAcc) { return pParentWidgetAcc->GetNextWidget(NULL); } } return NULL; } FX_BOOL CXFA_FFWidget::IsAncestorOf(CXFA_FFWidget* pWidget) { if (!pWidget) { return FALSE; } CXFA_Node* pNode = m_pDataAcc->GetNode(); CXFA_Node* pChildNode = pWidget->GetDataAcc()->GetNode(); while (pChildNode) { if (pChildNode == pNode) { return TRUE; } pChildNode = pChildNode->GetNodeItem(XFA_NODEITEM_Parent); } return FALSE; } FX_BOOL CXFA_FFWidget::PtInActiveRect(FX_FLOAT fx, FX_FLOAT fy) { CFX_RectF rtWidget; GetWidgetRect(rtWidget); if (rtWidget.Contains(fx, fy)) { return TRUE; } return FALSE; } CXFA_FFDocView* CXFA_FFWidget::GetDocView() { return m_pDocView; } CXFA_FFDoc* CXFA_FFWidget::GetDoc() { return (CXFA_FFDoc*)m_pDocView->GetDoc(); } CXFA_FFApp* CXFA_FFWidget::GetApp() { return GetDoc()->GetApp(); } IXFA_AppProvider* CXFA_FFWidget::GetAppProvider() { return GetApp()->GetAppProvider(); } void CXFA_FFWidget::GetMinMaxWidth(FX_FLOAT fMinWidth, FX_FLOAT fMaxWidth) { fMinWidth = fMaxWidth = 0; FX_FLOAT fWidth = 0; if (m_pDataAcc->GetWidth(fWidth)) { fMinWidth = fMaxWidth = fWidth; } else { m_pDataAcc->GetMinWidth(fMinWidth); m_pDataAcc->GetMaxWidth(fMaxWidth); } } void CXFA_FFWidget::GetMinMaxHeight(FX_FLOAT fMinHeight, FX_FLOAT fMaxHeight) { fMinHeight = fMaxHeight = 0; FX_FLOAT fHeight = 0; if (m_pDataAcc->GetHeight(fHeight)) { fMinHeight = fMaxHeight = fHeight; } else { m_pDataAcc->GetMinHeight(fMinHeight); m_pDataAcc->GetMaxHeight(fMaxHeight); } } FX_BOOL CXFA_FFWidget::IsMatchVisibleStatus(uint32_t dwStatus) { return m_dwStatus & XFA_WIDGETSTATUS_Visible; } void CXFA_FFWidget::EventKillFocus() { if (m_dwStatus & XFA_WIDGETSTATUS_Access) { m_dwStatus &= ~XFA_WIDGETSTATUS_Access; return; } CXFA_EventParam eParam; eParam.m_eType = XFA_EVENT_Exit; eParam.m_pTarget = m_pDataAcc; m_pDataAcc->ProcessEvent(XFA_ATTRIBUTEENUM_Exit, &eParam); } FX_BOOL CXFA_FFWidget::IsButtonDown() { return (m_dwStatus & XFA_WIDGETSTATUS_ButtonDown) != 0; } void CXFA_FFWidget::SetButtonDown(FX_BOOL bSet) { bSet ? m_dwStatus |= XFA_WIDGETSTATUS_ButtonDown : m_dwStatus &= ~XFA_WIDGETSTATUS_ButtonDown; } int32_t XFA_StrokeTypeSetLineDash(CFX_Graphics* pGraphics, int32_t iStrokeType, int32_t iCapType) { switch (iStrokeType) { case XFA_ATTRIBUTEENUM_DashDot: { FX_FLOAT dashArray[] = {4, 1, 2, 1}; if (iCapType != XFA_ATTRIBUTEENUM_Butt) { dashArray[1] = 2; dashArray[3] = 2; } pGraphics->SetLineDash(0, dashArray, 4); return FX_DASHSTYLE_DashDot; } case XFA_ATTRIBUTEENUM_DashDotDot: { FX_FLOAT dashArray[] = {4, 1, 2, 1, 2, 1}; if (iCapType != XFA_ATTRIBUTEENUM_Butt) { dashArray[1] = 2; dashArray[3] = 2; dashArray[5] = 2; } pGraphics->SetLineDash(0, dashArray, 6); return FX_DASHSTYLE_DashDotDot; } case XFA_ATTRIBUTEENUM_Dashed: { FX_FLOAT dashArray[] = {5, 1}; if (iCapType != XFA_ATTRIBUTEENUM_Butt) { dashArray[1] = 2; } pGraphics->SetLineDash(0, dashArray, 2); return FX_DASHSTYLE_Dash; } case XFA_ATTRIBUTEENUM_Dotted: { FX_FLOAT dashArray[] = {2, 1}; if (iCapType != XFA_ATTRIBUTEENUM_Butt) { dashArray[1] = 2; } pGraphics->SetLineDash(0, dashArray, 2); return FX_DASHSTYLE_Dot; } default: break; } pGraphics->SetLineDash(FX_DASHSTYLE_Solid); return FX_DASHSTYLE_Solid; } CFX_GraphStateData::LineCap XFA_LineCapToFXGE(int32_t iLineCap) { switch (iLineCap) { case XFA_ATTRIBUTEENUM_Round: return CFX_GraphStateData::LineCapRound; case XFA_ATTRIBUTEENUM_Butt: return CFX_GraphStateData::LineCapButt; default: break; } return CFX_GraphStateData::LineCapSquare; } class CXFA_ImageRenderer { public: CXFA_ImageRenderer(); ~CXFA_ImageRenderer(); FX_BOOL Start(CFX_RenderDevice* pDevice, CFX_DIBSource* pDIBSource, FX_ARGB bitmap_argb, int bitmap_alpha, const CFX_Matrix* pImage2Device, uint32_t flags, int blendType = FXDIB_BLEND_NORMAL); FX_BOOL Continue(IFX_Pause* pPause); protected: CFX_RenderDevice* m_pDevice; int m_Status; CFX_Matrix m_ImageMatrix; CFX_DIBSource* m_pDIBSource; CFX_DIBitmap* m_pCloneConvert; int m_BitmapAlpha; FX_ARGB m_FillArgb; uint32_t m_Flags; CFX_ImageTransformer* m_pTransformer; void* m_DeviceHandle; int32_t m_BlendType; FX_BOOL m_Result; FX_BOOL m_bPrint; FX_BOOL StartDIBSource(); void CompositeDIBitmap(CFX_DIBitmap* pDIBitmap, int left, int top, FX_ARGB mask_argb, int bitmap_alpha, int blend_mode, int Transparency); }; CXFA_ImageRenderer::CXFA_ImageRenderer() { m_pDevice = NULL; m_Status = 0; m_pDIBSource = NULL; m_pCloneConvert = NULL; m_BitmapAlpha = 255; m_FillArgb = 0; m_Flags = 0; m_pTransformer = NULL; m_DeviceHandle = NULL; m_BlendType = FXDIB_BLEND_NORMAL; m_Result = TRUE; m_bPrint = FALSE; } CXFA_ImageRenderer::~CXFA_ImageRenderer() { delete m_pCloneConvert; delete m_pTransformer; if (m_DeviceHandle) m_pDevice->CancelDIBits(m_DeviceHandle); } FX_BOOL CXFA_ImageRenderer::Start(CFX_RenderDevice* pDevice, CFX_DIBSource* pDIBSource, FX_ARGB bitmap_argb, int bitmap_alpha, const CFX_Matrix* pImage2Device, uint32_t flags, int blendType) { m_pDevice = pDevice; m_pDIBSource = pDIBSource; m_FillArgb = bitmap_argb; m_BitmapAlpha = bitmap_alpha; m_ImageMatrix = *pImage2Device; m_Flags = flags; m_BlendType = blendType; return StartDIBSource(); } FX_BOOL CXFA_ImageRenderer::StartDIBSource() { if (m_pDevice->StartDIBits(m_pDIBSource, m_BitmapAlpha, m_FillArgb, &m_ImageMatrix, m_Flags, m_DeviceHandle, 0, NULL, m_BlendType)) { if (m_DeviceHandle) { m_Status = 3; return TRUE; } return FALSE; } CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect(); FX_RECT image_rect = image_rect_f.GetOutterRect(); int dest_width = image_rect.Width(); int dest_height = image_rect.Height(); if ((FXSYS_fabs(m_ImageMatrix.b) >= 0.5f || m_ImageMatrix.a == 0) || (FXSYS_fabs(m_ImageMatrix.c) >= 0.5f || m_ImageMatrix.d == 0)) { if (m_bPrint && !(m_pDevice->GetRenderCaps() & FXRC_BLEND_MODE)) { m_Result = FALSE; return FALSE; } CFX_DIBSource* pDib = m_pDIBSource; if (m_pDIBSource->HasAlpha() && !(m_pDevice->GetRenderCaps() & FXRC_ALPHA_IMAGE) && !(m_pDevice->GetRenderCaps() & FXRC_GET_BITS)) { m_pCloneConvert = m_pDIBSource->CloneConvert(FXDIB_Rgb); if (!m_pCloneConvert) { m_Result = FALSE; return FALSE; } pDib = m_pCloneConvert; } FX_RECT clip_box = m_pDevice->GetClipBox(); clip_box.Intersect(image_rect); m_Status = 2; m_pTransformer = new CFX_ImageTransformer; m_pTransformer->Start(pDib, &m_ImageMatrix, m_Flags, &clip_box); return TRUE; } if (m_ImageMatrix.a < 0) { dest_width = -dest_width; } if (m_ImageMatrix.d > 0) { dest_height = -dest_height; } int dest_left, dest_top; dest_left = dest_width > 0 ? image_rect.left : image_rect.right; dest_top = dest_height > 0 ? image_rect.top : image_rect.bottom; if (m_pDIBSource->IsOpaqueImage() && m_BitmapAlpha == 255) { if (m_pDevice->StretchDIBits(m_pDIBSource, dest_left, dest_top, dest_width, dest_height, m_Flags, NULL, m_BlendType)) { return FALSE; } } if (m_pDIBSource->IsAlphaMask()) { if (m_BitmapAlpha != 255) { m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha); } if (m_pDevice->StretchBitMask(m_pDIBSource, dest_left, dest_top, dest_width, dest_height, m_FillArgb, m_Flags)) { return FALSE; } } if (m_bPrint && !(m_pDevice->GetRenderCaps() & FXRC_BLEND_MODE)) { m_Result = FALSE; return TRUE; } FX_RECT clip_box = m_pDevice->GetClipBox(); FX_RECT dest_rect = clip_box; dest_rect.Intersect(image_rect); FX_RECT dest_clip( dest_rect.left - image_rect.left, dest_rect.top - image_rect.top, dest_rect.right - image_rect.left, dest_rect.bottom - image_rect.top); CFX_DIBitmap* pStretched = m_pDIBSource->StretchTo(dest_width, dest_height, m_Flags, &dest_clip); if (pStretched) { CompositeDIBitmap(pStretched, dest_rect.left, dest_rect.top, m_FillArgb, m_BitmapAlpha, m_BlendType, FALSE); delete pStretched; pStretched = NULL; } return FALSE; } FX_BOOL CXFA_ImageRenderer::Continue(IFX_Pause* pPause) { if (m_Status == 2) { if (m_pTransformer->Continue(pPause)) { return TRUE; } CFX_DIBitmap* pBitmap = m_pTransformer->m_Storer.Detach(); if (pBitmap == NULL) { return FALSE; } if (pBitmap->IsAlphaMask()) { if (m_BitmapAlpha != 255) { m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha); } m_Result = m_pDevice->SetBitMask(pBitmap, m_pTransformer->m_ResultLeft, m_pTransformer->m_ResultTop, m_FillArgb); } else { if (m_BitmapAlpha != 255) { pBitmap->MultiplyAlpha(m_BitmapAlpha); } m_Result = m_pDevice->SetDIBits(pBitmap, m_pTransformer->m_ResultLeft, m_pTransformer->m_ResultTop, m_BlendType); } delete pBitmap; return FALSE; } else if (m_Status == 3) { return m_pDevice->ContinueDIBits(m_DeviceHandle, pPause); } return FALSE; } void CXFA_ImageRenderer::CompositeDIBitmap(CFX_DIBitmap* pDIBitmap, int left, int top, FX_ARGB mask_argb, int bitmap_alpha, int blend_mode, int Transparency) { if (pDIBitmap == NULL) { return; } FX_BOOL bIsolated = Transparency & PDFTRANS_ISOLATED; FX_BOOL bGroup = Transparency & PDFTRANS_GROUP; if (blend_mode == FXDIB_BLEND_NORMAL) { if (!pDIBitmap->IsAlphaMask()) { if (bitmap_alpha < 255) { pDIBitmap->MultiplyAlpha(bitmap_alpha); } if (m_pDevice->SetDIBits(pDIBitmap, left, top)) { return; } } else { uint32_t fill_argb = (mask_argb); if (bitmap_alpha < 255) { ((uint8_t*)&fill_argb)[3] = ((uint8_t*)&fill_argb)[3] * bitmap_alpha / 255; } if (m_pDevice->SetBitMask(pDIBitmap, left, top, fill_argb)) { return; } } } FX_BOOL bBackAlphaRequired = blend_mode && bIsolated; FX_BOOL bGetBackGround = ((m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT)) || (!(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT) && (m_pDevice->GetRenderCaps() & FXRC_GET_BITS) && !bBackAlphaRequired); if (bGetBackGround) { if (bIsolated || !bGroup) { if (pDIBitmap->IsAlphaMask()) { return; } m_pDevice->SetDIBits(pDIBitmap, left, top, blend_mode); } else { FX_RECT rect(left, top, left + pDIBitmap->GetWidth(), top + pDIBitmap->GetHeight()); rect.Intersect(m_pDevice->GetClipBox()); CFX_DIBitmap* pClone = NULL; FX_BOOL bClone = FALSE; if (m_pDevice->GetBackDrop() && m_pDevice->GetBitmap()) { bClone = TRUE; pClone = m_pDevice->GetBackDrop()->Clone(&rect); CFX_DIBitmap* pForeBitmap = m_pDevice->GetBitmap(); pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(), pForeBitmap, rect.left, rect.top); left = left >= 0 ? 0 : left; top = top >= 0 ? 0 : top; if (!pDIBitmap->IsAlphaMask()) pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(), pDIBitmap, left, top, blend_mode); else pClone->CompositeMask(0, 0, pClone->GetWidth(), pClone->GetHeight(), pDIBitmap, mask_argb, left, top, blend_mode); } else { pClone = pDIBitmap; } if (m_pDevice->GetBackDrop()) { m_pDevice->SetDIBits(pClone, rect.left, rect.top); } else { if (pDIBitmap->IsAlphaMask()) { return; } m_pDevice->SetDIBits(pDIBitmap, rect.left, rect.top, blend_mode); } if (bClone) { delete pClone; } } return; } if (pDIBitmap->HasAlpha() && !(m_pDevice->GetRenderCaps() & FXRC_ALPHA_IMAGE)) { CFX_DIBitmap* pCloneConvert = pDIBitmap->CloneConvert(FXDIB_Rgb); if (!pCloneConvert) { return; } CXFA_ImageRenderer imageRender; FX_BOOL bRet = imageRender.Start(m_pDevice, pCloneConvert, m_FillArgb, m_BitmapAlpha, &m_ImageMatrix, m_Flags); while (bRet) { bRet = imageRender.Continue(NULL); } delete pCloneConvert; return; } } void XFA_DrawImage(CFX_Graphics* pGS, const CFX_RectF& rtImage, CFX_Matrix* pMatrix, CFX_DIBitmap* pDIBitmap, int32_t iAspect, int32_t iImageXDpi, int32_t iImageYDpi, int32_t iHorzAlign, int32_t iVertAlign) { if (rtImage.IsEmpty()) { return; } if (!pDIBitmap || !pDIBitmap->GetBuffer()) { return; } FX_FLOAT fWidth = XFA_UnitPx2Pt((FX_FLOAT)pDIBitmap->GetWidth(), (FX_FLOAT)iImageXDpi); FX_FLOAT fHeight = XFA_UnitPx2Pt((FX_FLOAT)pDIBitmap->GetHeight(), (FX_FLOAT)iImageYDpi); CFX_RectF rtFit; rtFit.Set(rtImage.left, rtImage.top, fWidth, fHeight); switch (iAspect) { case XFA_ATTRIBUTEENUM_Fit: { FX_FLOAT f1 = rtImage.height / rtFit.height; FX_FLOAT f2 = rtImage.width / rtFit.width; f1 = std::min(f1, f2); rtFit.height = rtFit.height * f1; rtFit.width = rtFit.width * f1; } break; case XFA_ATTRIBUTEENUM_Actual: break; case XFA_ATTRIBUTEENUM_Height: { FX_FLOAT f1 = rtImage.height / rtFit.height; rtFit.height = rtImage.height; rtFit.width = f1 * rtFit.width; } break; case XFA_ATTRIBUTEENUM_None: rtFit.height = rtImage.height; rtFit.width = rtImage.width; break; case XFA_ATTRIBUTEENUM_Width: { FX_FLOAT f1 = rtImage.width / rtFit.width; rtFit.width = rtImage.width; rtFit.height = rtFit.height * f1; } break; } if (iHorzAlign == XFA_ATTRIBUTEENUM_Center) { rtFit.left += (rtImage.width - rtFit.width) / 2; } else if (iHorzAlign == XFA_ATTRIBUTEENUM_Right) { rtFit.left = rtImage.right() - rtFit.width; } if (iVertAlign == XFA_ATTRIBUTEENUM_Middle) { rtFit.top += (rtImage.height - rtFit.height) / 2; } else if (iVertAlign == XFA_ATTRIBUTEENUM_Bottom) { rtFit.top = rtImage.bottom() - rtImage.height; } CFX_RenderDevice* pRenderDevice = pGS->GetRenderDevice(); pRenderDevice->SaveState(); CFX_PathData path; path.AppendRect(rtImage.left, rtImage.bottom(), rtImage.right(), rtImage.top); pRenderDevice->SetClip_PathFill(&path, pMatrix, FXFILL_WINDING); CFX_Matrix mtImage(1, 0, 0, -1, 0, 1); mtImage.Concat(rtFit.width, 0, 0, rtFit.height, rtFit.left, rtFit.top); mtImage.Concat(*pMatrix); CXFA_ImageRenderer imageRender; FX_BOOL bRet = imageRender.Start(pRenderDevice, pDIBitmap, 0, 255, &mtImage, FXDIB_INTERPOL); while (bRet) { bRet = imageRender.Continue(NULL); } pRenderDevice->RestoreState(); } static const uint8_t g_inv_base64[128] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, }; static uint8_t* XFA_RemoveBase64Whitespace(const uint8_t* pStr, int32_t iLen) { uint8_t* pCP; int32_t i = 0, j = 0; if (iLen == 0) { iLen = FXSYS_strlen((FX_CHAR*)pStr); } pCP = FX_Alloc(uint8_t, iLen + 1); for (; i < iLen; i++) { if ((pStr[i] & 128) == 0) { if (g_inv_base64[pStr[i]] != 0xFF || pStr[i] == '=') { pCP[j++] = pStr[i]; } } } pCP[j] = '\0'; return pCP; } static int32_t XFA_Base64Decode(const FX_CHAR* pStr, uint8_t* pOutBuffer) { if (pStr == NULL) { return 0; } uint8_t* pBuffer = XFA_RemoveBase64Whitespace((uint8_t*)pStr, FXSYS_strlen((FX_CHAR*)pStr)); if (pBuffer == NULL) { return 0; } int32_t iLen = FXSYS_strlen((FX_CHAR*)pBuffer); int32_t i = 0, j = 0; uint32_t dwLimb = 0; for (; i + 3 < iLen; i += 4) { if (pBuffer[i] == '=' || pBuffer[i + 1] == '=' || pBuffer[i + 2] == '=' || pBuffer[i + 3] == '=') { if (pBuffer[i] == '=' || pBuffer[i + 1] == '=') { break; } if (pBuffer[i + 2] == '=') { dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 6) | ((uint32_t)g_inv_base64[pBuffer[i + 1]]); pOutBuffer[j] = (uint8_t)(dwLimb >> 4) & 0xFF; j++; } else { dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 12) | ((uint32_t)g_inv_base64[pBuffer[i + 1]] << 6) | ((uint32_t)g_inv_base64[pBuffer[i + 2]]); pOutBuffer[j] = (uint8_t)(dwLimb >> 10) & 0xFF; pOutBuffer[j + 1] = (uint8_t)(dwLimb >> 2) & 0xFF; j += 2; } } else { dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 18) | ((uint32_t)g_inv_base64[pBuffer[i + 1]] << 12) | ((uint32_t)g_inv_base64[pBuffer[i + 2]] << 6) | ((uint32_t)g_inv_base64[pBuffer[i + 3]]); pOutBuffer[j] = (uint8_t)(dwLimb >> 16) & 0xff; pOutBuffer[j + 1] = (uint8_t)(dwLimb >> 8) & 0xff; pOutBuffer[j + 2] = (uint8_t)(dwLimb)&0xff; j += 3; } } FX_Free(pBuffer); return j; } static const FX_CHAR g_base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; FX_CHAR* XFA_Base64Encode(const uint8_t* buf, int32_t buf_len) { FX_CHAR* out = NULL; int i, j; uint32_t limb; out = FX_Alloc(FX_CHAR, ((buf_len * 8 + 5) / 6) + 5); for (i = 0, j = 0, limb = 0; i + 2 < buf_len; i += 3, j += 4) { limb = ((uint32_t)buf[i] << 16) | ((uint32_t)buf[i + 1] << 8) | ((uint32_t)buf[i + 2]); out[j] = g_base64_chars[(limb >> 18) & 63]; out[j + 1] = g_base64_chars[(limb >> 12) & 63]; out[j + 2] = g_base64_chars[(limb >> 6) & 63]; out[j + 3] = g_base64_chars[(limb)&63]; } switch (buf_len - i) { case 0: break; case 1: limb = ((uint32_t)buf[i]); out[j++] = g_base64_chars[(limb >> 2) & 63]; out[j++] = g_base64_chars[(limb << 4) & 63]; out[j++] = '='; out[j++] = '='; break; case 2: limb = ((uint32_t)buf[i] << 8) | ((uint32_t)buf[i + 1]); out[j++] = g_base64_chars[(limb >> 10) & 63]; out[j++] = g_base64_chars[(limb >> 4) & 63]; out[j++] = g_base64_chars[(limb << 2) & 63]; out[j++] = '='; break; default: break; } out[j] = '\0'; return out; } FXCODEC_IMAGE_TYPE XFA_GetImageType(const CFX_WideString& wsType) { CFX_WideString wsContentType(wsType); wsContentType.MakeLower(); if (wsContentType == FX_WSTRC(L"image/jpg")) { return FXCODEC_IMAGE_JPG; } if (wsContentType == FX_WSTRC(L"image/png")) { return FXCODEC_IMAGE_PNG; } if (wsContentType == FX_WSTRC(L"image/gif")) { return FXCODEC_IMAGE_GIF; } if (wsContentType == FX_WSTRC(L"image/bmp")) { return FXCODEC_IMAGE_BMP; } if (wsContentType == FX_WSTRC(L"image/tif")) { return FXCODEC_IMAGE_TIF; } return FXCODEC_IMAGE_UNKNOWN; } CFX_DIBitmap* XFA_LoadImageData(CXFA_FFDoc* pDoc, CXFA_Image* pImage, FX_BOOL& bNameImage, int32_t& iImageXDpi, int32_t& iImageYDpi) { CFX_WideString wsHref; pImage->GetHref(wsHref); CFX_WideString wsImage; pImage->GetContent(wsImage); if (wsHref.IsEmpty() && wsImage.IsEmpty()) { return NULL; } CFX_WideString wsContentType; pImage->GetContentType(wsContentType); FXCODEC_IMAGE_TYPE type = XFA_GetImageType(wsContentType); CFX_ByteString bsContent; uint8_t* pImageBuffer = NULL; IFX_FileRead* pImageFileRead = NULL; if (wsImage.GetLength() > 0) { XFA_ATTRIBUTEENUM iEncoding = (XFA_ATTRIBUTEENUM)pImage->GetTransferEncoding(); if (iEncoding == XFA_ATTRIBUTEENUM_Base64) { CFX_ByteString bsData = wsImage.UTF8Encode(); int32_t iLength = bsData.GetLength(); pImageBuffer = FX_Alloc(uint8_t, iLength); int32_t iRead = XFA_Base64Decode(bsData.c_str(), pImageBuffer); if (iRead > 0) { pImageFileRead = FX_CreateMemoryStream(pImageBuffer, iRead); } } else { bsContent = CFX_ByteString::FromUnicode(wsImage); pImageFileRead = FX_CreateMemoryStream( const_cast<uint8_t*>(bsContent.raw_str()), bsContent.GetLength()); } } else { CFX_WideString wsURL = wsHref; if (wsURL.Left(7) != FX_WSTRC(L"http://") && wsURL.Left(6) != FX_WSTRC(L"ftp://")) { CFX_DIBitmap* pBitmap = pDoc->GetPDFNamedImage(wsURL.AsStringC(), iImageXDpi, iImageYDpi); if (pBitmap) { bNameImage = TRUE; return pBitmap; } } pImageFileRead = pDoc->GetDocProvider()->OpenLinkedFile(pDoc, wsURL); } if (!pImageFileRead) { FX_Free(pImageBuffer); return NULL; } bNameImage = FALSE; CFX_DIBitmap* pBitmap = XFA_LoadImageFromBuffer(pImageFileRead, type, iImageXDpi, iImageYDpi); FX_Free(pImageBuffer); pImageFileRead->Release(); return pBitmap; } static FXDIB_Format XFA_GetDIBFormat(FXCODEC_IMAGE_TYPE type, int32_t iComponents, int32_t iBitsPerComponent) { FXDIB_Format dibFormat = FXDIB_Argb; switch (type) { case FXCODEC_IMAGE_BMP: case FXCODEC_IMAGE_JPG: case FXCODEC_IMAGE_TIF: { dibFormat = FXDIB_Rgb32; int32_t bpp = iComponents * iBitsPerComponent; if (bpp <= 24) { dibFormat = FXDIB_Rgb; } } break; case FXCODEC_IMAGE_PNG: default: break; } return dibFormat; } CFX_DIBitmap* XFA_LoadImageFromBuffer(IFX_FileRead* pImageFileRead, FXCODEC_IMAGE_TYPE type, int32_t& iImageXDpi, int32_t& iImageYDpi) { CFX_GEModule* pGeModule = CFX_GEModule::Get(); if (!pGeModule) { return NULL; } CCodec_ModuleMgr* pCodecMgr = pGeModule->GetCodecModule(); if (!pCodecMgr) { return NULL; } CFX_DIBAttribute dibAttr; CFX_DIBitmap* pBitmap = NULL; CCodec_ProgressiveDecoder* pProgressiveDecoder = pCodecMgr->CreateProgressiveDecoder(); pProgressiveDecoder->LoadImageInfo(pImageFileRead, type, &dibAttr); switch (dibAttr.m_wDPIUnit) { case FXCODEC_RESUNIT_CENTIMETER: dibAttr.m_nXDPI = (int32_t)(dibAttr.m_nXDPI * 2.54f); dibAttr.m_nYDPI = (int32_t)(dibAttr.m_nYDPI * 2.54f); break; case FXCODEC_RESUNIT_METER: dibAttr.m_nXDPI = (int32_t)(dibAttr.m_nXDPI / (FX_FLOAT)100 * 2.54f); dibAttr.m_nYDPI = (int32_t)(dibAttr.m_nYDPI / (FX_FLOAT)100 * 2.54f); break; default: break; } iImageXDpi = dibAttr.m_nXDPI > 1 ? dibAttr.m_nXDPI : (96); iImageYDpi = dibAttr.m_nYDPI > 1 ? dibAttr.m_nYDPI : (96); if (pProgressiveDecoder->GetWidth() > 0 && pProgressiveDecoder->GetHeight() > 0) { type = pProgressiveDecoder->GetType(); int32_t iComponents = pProgressiveDecoder->GetNumComponents(); int32_t iBpc = pProgressiveDecoder->GetBPC(); FXDIB_Format dibFormat = XFA_GetDIBFormat(type, iComponents, iBpc); pBitmap = new CFX_DIBitmap(); pBitmap->Create(pProgressiveDecoder->GetWidth(), pProgressiveDecoder->GetHeight(), dibFormat); pBitmap->Clear(0xffffffff); int32_t nFrames; if ((pProgressiveDecoder->GetFrames(nFrames) == FXCODEC_STATUS_DECODE_READY) && (nFrames > 0)) { pProgressiveDecoder->StartDecode(pBitmap, 0, 0, pBitmap->GetWidth(), pBitmap->GetHeight()); pProgressiveDecoder->ContinueDecode(); } } delete pProgressiveDecoder; return pBitmap; } void XFA_RectWidthoutMargin(CFX_RectF& rt, const CXFA_Margin& mg, FX_BOOL bUI) { if (!mg) { return; } FX_FLOAT fLeftInset, fTopInset, fRightInset, fBottomInset; mg.GetLeftInset(fLeftInset); mg.GetTopInset(fTopInset); mg.GetRightInset(fRightInset); mg.GetBottomInset(fBottomInset); rt.Deflate(fLeftInset, fTopInset, fRightInset, fBottomInset); } CXFA_FFWidget* XFA_GetWidgetFromLayoutItem(CXFA_LayoutItem* pLayoutItem) { XFA_ELEMENT iType = pLayoutItem->GetFormNode()->GetClassID(); if (XFA_IsCreateWidget(iType)) { return static_cast<CXFA_FFWidget*>(pLayoutItem); } return nullptr; } FX_BOOL XFA_IsCreateWidget(XFA_ELEMENT iType) { return iType == XFA_ELEMENT_Field || iType == XFA_ELEMENT_Draw || iType == XFA_ELEMENT_Subform || iType == XFA_ELEMENT_ExclGroup; } static void XFA_BOX_GetPath_Arc(CXFA_Box box, CFX_RectF rtDraw, CFX_Path& fillPath, uint32_t dwFlags) { FX_FLOAT a, b; a = rtDraw.width / 2.0f; b = rtDraw.height / 2.0f; if (box.IsCircular() || (dwFlags & XFA_DRAWBOX_ForceRound) != 0) { a = b = std::min(a, b); } CFX_PointF center = rtDraw.Center(); rtDraw.left = center.x - a; rtDraw.top = center.y - b; rtDraw.width = a + a; rtDraw.height = b + b; FX_FLOAT startAngle = 0, sweepAngle = 360; FX_BOOL bStart = box.GetStartAngle(startAngle); FX_BOOL bEnd = box.GetSweepAngle(sweepAngle); if (!bStart && !bEnd) { fillPath.AddEllipse(rtDraw); return; } startAngle = -startAngle * FX_PI / 180.0f; sweepAngle = -sweepAngle * FX_PI / 180.0f; fillPath.AddArc(rtDraw.left, rtDraw.top, rtDraw.width, rtDraw.height, startAngle, sweepAngle); } static void XFA_BOX_GetPath(CXFA_Box box, const CXFA_StrokeArray& strokes, CFX_RectF rtWidget, CFX_Path& path, int32_t nIndex, FX_BOOL bStart, FX_BOOL bCorner) { ASSERT(nIndex >= 0 && nIndex < 8); FX_BOOL bInverted, bRound; FX_FLOAT fRadius1, fRadius2, sx, sy, vx, vy, nx, ny, offsetY, offsetX, offsetEX, offsetEY; CFX_PointF cpStart, cp, cp1, cp2; CFX_RectF rtRadius; int32_t n = (nIndex & 1) ? nIndex - 1 : nIndex; CXFA_Corner corner1(strokes[n].GetNode()); CXFA_Corner corner2(strokes[(n + 2) % 8].GetNode()); fRadius1 = bCorner ? corner1.GetRadius() : 0; fRadius2 = bCorner ? corner2.GetRadius() : 0; bInverted = corner1.IsInverted(); offsetY = 0.0f; offsetX = 0.0f; bRound = corner1.GetJoinType() == XFA_ATTRIBUTEENUM_Round; FX_FLOAT halfAfter = 0.0f; FX_FLOAT halfBefore = 0.0f; CXFA_Stroke stroke = strokes[nIndex]; if (stroke.IsCorner()) { CXFA_Stroke edgeBefore = strokes[(nIndex + 1 * 8 - 1) % 8]; CXFA_Stroke edgeAfter = strokes[nIndex + 1]; if (stroke.IsInverted()) { if (!stroke.SameStyles(edgeBefore)) { halfBefore = edgeBefore.GetThickness() / 2; } if (!stroke.SameStyles(edgeAfter)) { halfAfter = edgeAfter.GetThickness() / 2; } } } else { CXFA_Stroke edgeBefore = strokes[(nIndex + 8 - 2) % 8]; CXFA_Stroke edgeAfter = strokes[(nIndex + 2) % 8]; if (!bRound && !bInverted) { { halfBefore = edgeBefore.GetThickness() / 2; } { halfAfter = edgeAfter.GetThickness() / 2; } } } offsetEX = 0.0f; offsetEY = 0.0f; if (bRound) { sy = FX_PI / 2; } switch (nIndex) { case 0: case 1: cp1 = rtWidget.TopLeft(); cp2 = rtWidget.TopRight(); if (nIndex == 0) { cpStart.x = cp1.x - halfBefore; cpStart.y = cp1.y + fRadius1, offsetY = -halfAfter; } else { cpStart.x = cp1.x + fRadius1 - halfBefore, cpStart.y = cp1.y, offsetEX = halfAfter; } vx = 1, vy = 1; nx = -1, ny = 0; if (bRound) { sx = bInverted ? FX_PI / 2 : FX_PI; } else { sx = 1, sy = 0; } break; case 2: case 3: cp1 = rtWidget.TopRight(); cp2 = rtWidget.BottomRight(); if (nIndex == 2) { cpStart.x = cp1.x - fRadius1, cpStart.y = cp1.y - halfBefore, offsetX = halfAfter; } else { cpStart.x = cp1.x, cpStart.y = cp1.y + fRadius1 - halfBefore, offsetEY = halfAfter; } vx = -1, vy = 1; nx = 0, ny = -1; if (bRound) { sx = bInverted ? FX_PI : FX_PI * 3 / 2; } else { sx = 0, sy = 1; } break; case 4: case 5: cp1 = rtWidget.BottomRight(); cp2 = rtWidget.BottomLeft(); if (nIndex == 4) { cpStart.x = cp1.x + halfBefore, cpStart.y = cp1.y - fRadius1, offsetY = halfAfter; } else { cpStart.x = cp1.x - fRadius1 + halfBefore, cpStart.y = cp1.y, offsetEX = -halfAfter; } vx = -1, vy = -1; nx = 1, ny = 0; if (bRound) { sx = bInverted ? FX_PI * 3 / 2 : 0; } else { sx = -1, sy = 0; } break; case 6: case 7: cp1 = rtWidget.BottomLeft(); cp2 = rtWidget.TopLeft(); if (nIndex == 6) { cpStart.x = cp1.x + fRadius1, cpStart.y = cp1.y + halfBefore, offsetX = -halfAfter; } else { cpStart.x = cp1.x, cpStart.y = cp1.y - fRadius1 + halfBefore, offsetEY = -halfAfter; } vx = 1, vy = -1; nx = 0, ny = 1; if (bRound) { sx = bInverted ? 0 : FX_PI / 2; } else { sx = 0, sy = -1; } break; } if (bStart) { path.MoveTo(cpStart.x, cpStart.y); } if (nIndex & 1) { path.LineTo(cp2.x + fRadius2 * nx + offsetEX, cp2.y + fRadius2 * ny + offsetEY); return; } if (bRound) { if (fRadius1 < 0) { sx -= FX_PI; } if (bInverted) { sy *= -1; } rtRadius.Set(cp1.x + offsetX * 2, cp1.y + offsetY * 2, fRadius1 * 2 * vx - offsetX * 2, fRadius1 * 2 * vy - offsetY * 2); rtRadius.Normalize(); if (bInverted) { rtRadius.Offset(-fRadius1 * vx, -fRadius1 * vy); } path.ArcTo(rtRadius.left, rtRadius.top, rtRadius.width, rtRadius.height, sx, sy); } else { if (bInverted) { cp.x = cp1.x + fRadius1 * vx, cp.y = cp1.y + fRadius1 * vy; } else { cp = cp1; } path.LineTo(cp.x, cp.y); path.LineTo(cp1.x + fRadius1 * sx + offsetX, cp1.y + fRadius1 * sy + offsetY); } } static void XFA_BOX_GetFillPath(CXFA_Box box, const CXFA_StrokeArray& strokes, CFX_RectF rtWidget, CFX_Path& fillPath, uint16_t dwFlags) { if (box.IsArc() || (dwFlags & XFA_DRAWBOX_ForceRound) != 0) { CXFA_Edge edge = box.GetEdge(0); FX_FLOAT fThickness = edge.GetThickness(); if (fThickness < 0) { fThickness = 0; } FX_FLOAT fHalf = fThickness / 2; int32_t iHand = box.GetHand(); if (iHand == XFA_ATTRIBUTEENUM_Left) { rtWidget.Inflate(fHalf, fHalf); } else if (iHand == XFA_ATTRIBUTEENUM_Right) { rtWidget.Deflate(fHalf, fHalf); } XFA_BOX_GetPath_Arc(box, rtWidget, fillPath, dwFlags); return; } FX_BOOL bSameStyles = TRUE; int32_t i; CXFA_Stroke stroke1 = strokes[0]; for (i = 1; i < 8; i++) { CXFA_Stroke stroke2 = strokes[i]; if (!stroke1.SameStyles(stroke2)) { bSameStyles = FALSE; break; } stroke1 = stroke2; } if (bSameStyles) { stroke1 = strokes[0]; for (i = 2; i < 8; i += 2) { CXFA_Stroke stroke2 = strokes[i]; if (!stroke1.SameStyles(stroke2, XFA_STROKE_SAMESTYLE_NoPresence | XFA_STROKE_SAMESTYLE_Corner)) { bSameStyles = FALSE; break; } stroke1 = stroke2; } if (bSameStyles) { stroke1 = strokes[0]; if (stroke1.IsInverted()) { bSameStyles = FALSE; } if (stroke1.GetJoinType() != XFA_ATTRIBUTEENUM_Square) { bSameStyles = FALSE; } } } if (bSameStyles) { fillPath.AddRectangle(rtWidget.left, rtWidget.top, rtWidget.width, rtWidget.height); return; } FX_BOOL bInverted, bRound; FX_FLOAT fRadius1, fRadius2, sx, sy, vx, vy, nx, ny; CFX_PointF cp, cp1, cp2; CFX_RectF rtRadius; for (int32_t i = 0; i < 8; i += 2) { CXFA_Corner corner1(strokes[i].GetNode()); CXFA_Corner corner2(strokes[(i + 2) % 8].GetNode()); fRadius1 = corner1.GetRadius(); fRadius2 = corner2.GetRadius(); bInverted = corner1.IsInverted(); bRound = corner1.GetJoinType() == XFA_ATTRIBUTEENUM_Round; if (bRound) { sy = FX_PI / 2; } switch (i) { case 0: cp1 = rtWidget.TopLeft(); cp2 = rtWidget.TopRight(); vx = 1, vy = 1; nx = -1, ny = 0; if (bRound) { sx = bInverted ? FX_PI / 2 : FX_PI; } else { sx = 1, sy = 0; } break; case 2: cp1 = rtWidget.TopRight(); cp2 = rtWidget.BottomRight(); vx = -1, vy = 1; nx = 0, ny = -1; if (bRound) { sx = bInverted ? FX_PI : FX_PI * 3 / 2; } else { sx = 0, sy = 1; } break; case 4: cp1 = rtWidget.BottomRight(); cp2 = rtWidget.BottomLeft(); vx = -1, vy = -1; nx = 1, ny = 0; if (bRound) { sx = bInverted ? FX_PI * 3 / 2 : 0; } else { sx = -1, sy = 0; } break; case 6: cp1 = rtWidget.BottomLeft(); cp2 = rtWidget.TopLeft(); vx = 1, vy = -1; nx = 0, ny = 1; if (bRound) { sx = bInverted ? 0 : FX_PI / 2; } else { sx = 0, sy = -1; } break; } if (i == 0) { fillPath.MoveTo(cp1.x, cp1.y + fRadius1); } if (bRound) { if (fRadius1 < 0) { sx -= FX_PI; } if (bInverted) { sy *= -1; } rtRadius.Set(cp1.x, cp1.y, fRadius1 * 2 * vx, fRadius1 * 2 * vy); rtRadius.Normalize(); if (bInverted) { rtRadius.Offset(-fRadius1 * vx, -fRadius1 * vy); } fillPath.ArcTo(rtRadius.left, rtRadius.top, rtRadius.width, rtRadius.height, sx, sy); } else { if (bInverted) { cp.x = cp1.x + fRadius1 * vx, cp.y = cp1.y + fRadius1 * vy; } else { cp = cp1; } fillPath.LineTo(cp.x, cp.y); fillPath.LineTo(cp1.x + fRadius1 * sx, cp1.y + fRadius1 * sy); } fillPath.LineTo(cp2.x + fRadius2 * nx, cp2.y + fRadius2 * ny); } } static void XFA_BOX_Fill_Radial(CXFA_Box box, CFX_Graphics* pGS, CFX_Path& fillPath, CFX_RectF rtFill, CFX_Matrix* pMatrix) { CXFA_Fill fill = box.GetFill(); FX_ARGB crStart, crEnd; crStart = fill.GetColor(); int32_t iType = fill.GetRadial(crEnd); if (iType != XFA_ATTRIBUTEENUM_ToEdge) { FX_ARGB temp = crEnd; crEnd = crStart; crStart = temp; } CFX_Shading shading(rtFill.Center(), rtFill.Center(), 0, FXSYS_sqrt(rtFill.Width() * rtFill.Width() + rtFill.Height() * rtFill.Height()) / 2, TRUE, TRUE, crStart, crEnd); CFX_Color cr(&shading); pGS->SetFillColor(&cr); pGS->FillPath(&fillPath, FXFILL_WINDING, pMatrix); } static void XFA_BOX_Fill_Pattern(CXFA_Box box, CFX_Graphics* pGS, CFX_Path& fillPath, CFX_RectF rtFill, CFX_Matrix* pMatrix) { CXFA_Fill fill = box.GetFill(); FX_ARGB crStart, crEnd; crStart = fill.GetColor(); int32_t iType = fill.GetPattern(crEnd); FX_HatchStyle iHatch = FX_HATCHSTYLE_Cross; switch (iType) { case XFA_ATTRIBUTEENUM_CrossDiagonal: iHatch = FX_HATCHSTYLE_DiagonalCross; break; case XFA_ATTRIBUTEENUM_DiagonalLeft: iHatch = FX_HATCHSTYLE_ForwardDiagonal; break; case XFA_ATTRIBUTEENUM_DiagonalRight: iHatch = FX_HATCHSTYLE_BackwardDiagonal; break; case XFA_ATTRIBUTEENUM_Horizontal: iHatch = FX_HATCHSTYLE_Horizontal; break; case XFA_ATTRIBUTEENUM_Vertical: iHatch = FX_HATCHSTYLE_Vertical; break; default: break; } CFX_Pattern pattern(iHatch, crEnd, crStart); CFX_Color cr(&pattern); pGS->SetFillColor(&cr); pGS->FillPath(&fillPath, FXFILL_WINDING, pMatrix); } static void XFA_BOX_Fill_Linear(CXFA_Box box, CFX_Graphics* pGS, CFX_Path& fillPath, CFX_RectF rtFill, CFX_Matrix* pMatrix) { CXFA_Fill fill = box.GetFill(); FX_ARGB crStart = fill.GetColor(); FX_ARGB crEnd; int32_t iType = fill.GetLinear(crEnd); CFX_PointF ptStart; CFX_PointF ptEnd; switch (iType) { case XFA_ATTRIBUTEENUM_ToRight: ptStart = CFX_PointF(rtFill.left, rtFill.top); ptEnd = CFX_PointF(rtFill.right(), rtFill.top); break; case XFA_ATTRIBUTEENUM_ToBottom: ptStart = CFX_PointF(rtFill.left, rtFill.top); ptEnd = CFX_PointF(rtFill.left, rtFill.bottom()); break; case XFA_ATTRIBUTEENUM_ToLeft: ptStart = CFX_PointF(rtFill.right(), rtFill.top); ptEnd = CFX_PointF(rtFill.left, rtFill.top); break; case XFA_ATTRIBUTEENUM_ToTop: ptStart = CFX_PointF(rtFill.left, rtFill.bottom()); ptEnd = CFX_PointF(rtFill.left, rtFill.top); break; default: break; } CFX_Shading shading(ptStart, ptEnd, FALSE, FALSE, crStart, crEnd); CFX_Color cr(&shading); pGS->SetFillColor(&cr); pGS->FillPath(&fillPath, FXFILL_WINDING, pMatrix); } static void XFA_BOX_Fill(CXFA_Box box, const CXFA_StrokeArray& strokes, CFX_Graphics* pGS, const CFX_RectF& rtWidget, CFX_Matrix* pMatrix, uint32_t dwFlags) { CXFA_Fill fill = box.GetFill(); if (!fill || fill.GetPresence() != XFA_ATTRIBUTEENUM_Visible) { return; } pGS->SaveGraphState(); CFX_Path fillPath; fillPath.Create(); XFA_BOX_GetFillPath(box, strokes, rtWidget, fillPath, (dwFlags & XFA_DRAWBOX_ForceRound) != 0); fillPath.Close(); int32_t eType = fill.GetFillType(); switch (eType) { case XFA_ELEMENT_Radial: XFA_BOX_Fill_Radial(box, pGS, fillPath, rtWidget, pMatrix); break; case XFA_ELEMENT_Pattern: XFA_BOX_Fill_Pattern(box, pGS, fillPath, rtWidget, pMatrix); break; case XFA_ELEMENT_Linear: XFA_BOX_Fill_Linear(box, pGS, fillPath, rtWidget, pMatrix); break; default: { FX_ARGB cr; if (eType == XFA_ELEMENT_Stipple) { int32_t iRate = fill.GetStipple(cr); if (iRate == 0) { iRate = 100; } int32_t a = 0; FX_COLORREF rgb; ArgbDecode(cr, a, rgb); cr = ArgbEncode(iRate * a / 100, rgb); } else { cr = fill.GetColor(); } CFX_Color fillColor(cr); pGS->SetFillColor(&fillColor); pGS->FillPath(&fillPath, FXFILL_WINDING, pMatrix); } break; } pGS->RestoreGraphState(); } static void XFA_BOX_StrokePath(CXFA_Stroke stroke, CFX_Path* pPath, CFX_Graphics* pGS, CFX_Matrix* pMatrix) { if (!stroke || !stroke.IsVisible()) { return; } FX_FLOAT fThickness = stroke.GetThickness(); if (fThickness < 0.001f) { return; } pGS->SaveGraphState(); if (stroke.IsCorner() && fThickness > 2 * stroke.GetRadius()) { fThickness = 2 * stroke.GetRadius(); } pGS->SetLineWidth(fThickness, TRUE); pGS->SetLineCap(CFX_GraphStateData::LineCapButt); XFA_StrokeTypeSetLineDash(pGS, stroke.GetStrokeType(), XFA_ATTRIBUTEENUM_Butt); CFX_Color fxColor(stroke.GetColor()); pGS->SetStrokeColor(&fxColor); pGS->StrokePath(pPath, pMatrix); pGS->RestoreGraphState(); } static void XFA_BOX_StrokeArc(CXFA_Box box, CFX_Graphics* pGS, CFX_RectF rtWidget, CFX_Matrix* pMatrix, uint32_t dwFlags) { CXFA_Edge edge = box.GetEdge(0); if (!edge || !edge.IsVisible()) { return; } FX_BOOL bVisible = FALSE; FX_FLOAT fThickness = 0; int32_t i3DType = box.Get3DStyle(bVisible, fThickness); if (i3DType) { if (bVisible && fThickness >= 0.001f) { dwFlags |= XFA_DRAWBOX_Lowered3D; } } FX_FLOAT fHalf = edge.GetThickness() / 2; if (fHalf < 0) { fHalf = 0; } int32_t iHand = box.GetHand(); if (iHand == XFA_ATTRIBUTEENUM_Left) { rtWidget.Inflate(fHalf, fHalf); } else if (iHand == XFA_ATTRIBUTEENUM_Right) { rtWidget.Deflate(fHalf, fHalf); } if ((dwFlags & XFA_DRAWBOX_ForceRound) == 0 || (dwFlags & XFA_DRAWBOX_Lowered3D) == 0) { if (fHalf < 0.001f) { return; } CFX_Path arcPath; arcPath.Create(); XFA_BOX_GetPath_Arc(box, rtWidget, arcPath, dwFlags); XFA_BOX_StrokePath(edge, &arcPath, pGS, pMatrix); return; } pGS->SaveGraphState(); pGS->SetLineWidth(fHalf); FX_FLOAT a, b; a = rtWidget.width / 2.0f; b = rtWidget.height / 2.0f; if (dwFlags & XFA_DRAWBOX_ForceRound) { a = b = std::min(a, b); } CFX_PointF center = rtWidget.Center(); rtWidget.left = center.x - a; rtWidget.top = center.y - b; rtWidget.width = a + a; rtWidget.height = b + b; FX_FLOAT startAngle = 0, sweepAngle = 360; startAngle = startAngle * FX_PI / 180.0f; sweepAngle = -sweepAngle * FX_PI / 180.0f; CFX_Path arcPath; arcPath.Create(); arcPath.AddArc(rtWidget.left, rtWidget.top, rtWidget.width, rtWidget.height, 3.0f * FX_PI / 4.0f, FX_PI); CFX_Color cr(0xFF808080); pGS->SetStrokeColor(&cr); pGS->StrokePath(&arcPath, pMatrix); arcPath.Clear(); arcPath.AddArc(rtWidget.left, rtWidget.top, rtWidget.width, rtWidget.height, -1.0f * FX_PI / 4.0f, FX_PI); cr.Set(0xFFFFFFFF); pGS->SetStrokeColor(&cr); pGS->StrokePath(&arcPath, pMatrix); rtWidget.Deflate(fHalf, fHalf); arcPath.Clear(); arcPath.AddArc(rtWidget.left, rtWidget.top, rtWidget.width, rtWidget.height, 3.0f * FX_PI / 4.0f, FX_PI); cr.Set(0xFF404040); pGS->SetStrokeColor(&cr); pGS->StrokePath(&arcPath, pMatrix); arcPath.Clear(); arcPath.AddArc(rtWidget.left, rtWidget.top, rtWidget.width, rtWidget.height, -1.0f * FX_PI / 4.0f, FX_PI); cr.Set(0xFFC0C0C0); pGS->SetStrokeColor(&cr); pGS->StrokePath(&arcPath, pMatrix); pGS->RestoreGraphState(); } static void XFA_Draw3DRect(CFX_Graphics* pGraphic, const CFX_RectF& rt, FX_FLOAT fLineWidth, CFX_Matrix* pMatrix, FX_ARGB argbTopLeft, FX_ARGB argbBottomRight) { CFX_Color crLT(argbTopLeft); pGraphic->SetFillColor(&crLT); FX_FLOAT fBottom = rt.bottom(); FX_FLOAT fRight = rt.right(); CFX_Path pathLT; pathLT.Create(); pathLT.MoveTo(rt.left, fBottom); pathLT.LineTo(rt.left, rt.top); pathLT.LineTo(fRight, rt.top); pathLT.LineTo(fRight - fLineWidth, rt.top + fLineWidth); pathLT.LineTo(rt.left + fLineWidth, rt.top + fLineWidth); pathLT.LineTo(rt.left + fLineWidth, fBottom - fLineWidth); pathLT.LineTo(rt.left, fBottom); pGraphic->FillPath(&pathLT, FXFILL_WINDING, pMatrix); CFX_Color crRB(argbBottomRight); pGraphic->SetFillColor(&crRB); CFX_Path pathRB; pathRB.Create(); pathRB.MoveTo(fRight, rt.top); pathRB.LineTo(fRight, fBottom); pathRB.LineTo(rt.left, fBottom); pathRB.LineTo(rt.left + fLineWidth, fBottom - fLineWidth); pathRB.LineTo(fRight - fLineWidth, fBottom - fLineWidth); pathRB.LineTo(fRight - fLineWidth, rt.top + fLineWidth); pathRB.LineTo(fRight, rt.top); pGraphic->FillPath(&pathRB, FXFILL_WINDING, pMatrix); } static void XFA_BOX_Stroke_3DRect_Lowered(CFX_Graphics* pGS, CFX_RectF rt, FX_FLOAT fThickness, CFX_Matrix* pMatrix) { FX_FLOAT fHalfWidth = fThickness / 2.0f; CFX_RectF rtInner(rt); rtInner.Deflate(fHalfWidth, fHalfWidth); CFX_Color cr(0xFF000000); pGS->SetFillColor(&cr); CFX_Path path; path.Create(); path.AddRectangle(rt.left, rt.top, rt.width, rt.height); path.AddRectangle(rtInner.left, rtInner.top, rtInner.width, rtInner.height); pGS->FillPath(&path, FXFILL_ALTERNATE, pMatrix); XFA_Draw3DRect(pGS, rtInner, fHalfWidth, pMatrix, 0xFF808080, 0xFFC0C0C0); } static void XFA_BOX_Stroke_3DRect_Raised(CFX_Graphics* pGS, CFX_RectF rt, FX_FLOAT fThickness, CFX_Matrix* pMatrix) { FX_FLOAT fHalfWidth = fThickness / 2.0f; CFX_RectF rtInner(rt); rtInner.Deflate(fHalfWidth, fHalfWidth); CFX_Color cr(0xFF000000); pGS->SetFillColor(&cr); CFX_Path path; path.Create(); path.AddRectangle(rt.left, rt.top, rt.width, rt.height); path.AddRectangle(rtInner.left, rtInner.top, rtInner.width, rtInner.height); pGS->FillPath(&path, FXFILL_ALTERNATE, pMatrix); XFA_Draw3DRect(pGS, rtInner, fHalfWidth, pMatrix, 0xFFFFFFFF, 0xFF808080); } static void XFA_BOX_Stroke_3DRect_Etched(CFX_Graphics* pGS, CFX_RectF rt, FX_FLOAT fThickness, CFX_Matrix* pMatrix) { FX_FLOAT fHalfWidth = fThickness / 2.0f; XFA_Draw3DRect(pGS, rt, fThickness, pMatrix, 0xFF808080, 0xFFFFFFFF); CFX_RectF rtInner(rt); rtInner.Deflate(fHalfWidth, fHalfWidth); XFA_Draw3DRect(pGS, rtInner, fHalfWidth, pMatrix, 0xFFFFFFFF, 0xFF808080); } static void XFA_BOX_Stroke_3DRect_Embossed(CFX_Graphics* pGS, CFX_RectF rt, FX_FLOAT fThickness, CFX_Matrix* pMatrix) { FX_FLOAT fHalfWidth = fThickness / 2.0f; XFA_Draw3DRect(pGS, rt, fThickness, pMatrix, 0xFF808080, 0xFF000000); CFX_RectF rtInner(rt); rtInner.Deflate(fHalfWidth, fHalfWidth); XFA_Draw3DRect(pGS, rtInner, fHalfWidth, pMatrix, 0xFF000000, 0xFF808080); } static void XFA_BOX_Stroke_Rect(CXFA_Box box, const CXFA_StrokeArray& strokes, CFX_Graphics* pGS, CFX_RectF rtWidget, CFX_Matrix* pMatrix) { FX_BOOL bVisible = FALSE; FX_FLOAT fThickness = 0; int32_t i3DType = box.Get3DStyle(bVisible, fThickness); if (i3DType) { if (!bVisible || fThickness < 0.001f) { return; } switch (i3DType) { case XFA_ATTRIBUTEENUM_Lowered: XFA_BOX_Stroke_3DRect_Lowered(pGS, rtWidget, fThickness, pMatrix); break; case XFA_ATTRIBUTEENUM_Raised: XFA_BOX_Stroke_3DRect_Raised(pGS, rtWidget, fThickness, pMatrix); break; case XFA_ATTRIBUTEENUM_Etched: XFA_BOX_Stroke_3DRect_Etched(pGS, rtWidget, fThickness, pMatrix); break; case XFA_ATTRIBUTEENUM_Embossed: XFA_BOX_Stroke_3DRect_Embossed(pGS, rtWidget, fThickness, pMatrix); break; } return; } FX_BOOL bClose = FALSE; FX_BOOL bSameStyles = TRUE; int32_t i; CXFA_Stroke stroke1 = strokes[0]; for (i = 1; i < 8; i++) { CXFA_Stroke stroke2 = strokes[i]; if (!stroke1.SameStyles(stroke2)) { bSameStyles = FALSE; break; } stroke1 = stroke2; } if (bSameStyles) { stroke1 = strokes[0]; bClose = TRUE; for (i = 2; i < 8; i += 2) { CXFA_Stroke stroke2 = strokes[i]; if (!stroke1.SameStyles(stroke2, XFA_STROKE_SAMESTYLE_NoPresence | XFA_STROKE_SAMESTYLE_Corner)) { bSameStyles = FALSE; break; } stroke1 = stroke2; } if (bSameStyles) { stroke1 = strokes[0]; if (stroke1.IsInverted()) { bSameStyles = FALSE; } if (stroke1.GetJoinType() != XFA_ATTRIBUTEENUM_Square) { bSameStyles = FALSE; } } } FX_BOOL bStart = TRUE; CFX_Path path; path.Create(); for (i = 0; i < 8; i++) { CXFA_Stroke stroke1 = strokes[i]; if ((i % 1) == 0 && stroke1.GetRadius() < 0) { FX_BOOL bEmpty = path.IsEmpty(); if (!bEmpty) { XFA_BOX_StrokePath(stroke1, &path, pGS, pMatrix); path.Clear(); } bStart = TRUE; continue; } XFA_BOX_GetPath(box, strokes, rtWidget, path, i, bStart, !bSameStyles); CXFA_Stroke stroke2 = strokes[(i + 1) % 8]; bStart = !stroke1.SameStyles(stroke2); if (bStart) { XFA_BOX_StrokePath(stroke1, &path, pGS, pMatrix); path.Clear(); } } FX_BOOL bEmpty = path.IsEmpty(); if (!bEmpty) { if (bClose) { path.Close(); } XFA_BOX_StrokePath(strokes[7], &path, pGS, pMatrix); } } static void XFA_BOX_Stroke(CXFA_Box box, const CXFA_StrokeArray& strokes, CFX_Graphics* pGS, CFX_RectF rtWidget, CFX_Matrix* pMatrix, uint32_t dwFlags) { if (box.IsArc() || (dwFlags & XFA_DRAWBOX_ForceRound) != 0) { XFA_BOX_StrokeArc(box, pGS, rtWidget, pMatrix, dwFlags); return; } bool bVisible = false; for (int32_t j = 0; j < 4; j++) { if (strokes[j * 2 + 1].IsVisible()) { bVisible = true; break; } } if (!bVisible) { return; } for (int32_t i = 1; i < 8; i += 2) { CXFA_Edge edge(strokes[i].GetNode()); FX_FLOAT fThickness = edge.GetThickness(); if (fThickness < 0) { fThickness = 0; } FX_FLOAT fHalf = fThickness / 2; int32_t iHand = box.GetHand(); switch (i) { case 1: if (iHand == XFA_ATTRIBUTEENUM_Left) { rtWidget.top -= fHalf; rtWidget.height += fHalf; } else if (iHand == XFA_ATTRIBUTEENUM_Right) { rtWidget.top += fHalf; rtWidget.height -= fHalf; } break; case 3: if (iHand == XFA_ATTRIBUTEENUM_Left) { rtWidget.width += fHalf; } else if (iHand == XFA_ATTRIBUTEENUM_Right) { rtWidget.width -= fHalf; } break; case 5: if (iHand == XFA_ATTRIBUTEENUM_Left) { rtWidget.height += fHalf; } else if (iHand == XFA_ATTRIBUTEENUM_Right) { rtWidget.height -= fHalf; } break; case 7: if (iHand == XFA_ATTRIBUTEENUM_Left) { rtWidget.left -= fHalf; rtWidget.width += fHalf; } else if (iHand == XFA_ATTRIBUTEENUM_Right) { rtWidget.left += fHalf; rtWidget.width -= fHalf; } break; } } XFA_BOX_Stroke_Rect(box, strokes, pGS, rtWidget, pMatrix); } void XFA_DrawBox(CXFA_Box box, CFX_Graphics* pGS, const CFX_RectF& rtWidget, CFX_Matrix* pMatrix, uint32_t dwFlags) { if (!box || box.GetPresence() != XFA_ATTRIBUTEENUM_Visible) { return; } int32_t iType = box.GetClassID(); if (iType != XFA_ELEMENT_Arc && iType != XFA_ELEMENT_Border && iType != XFA_ELEMENT_Rectangle) { return; } CXFA_StrokeArray strokes; if (!(dwFlags & XFA_DRAWBOX_ForceRound) && iType != XFA_ELEMENT_Arc) { box.GetStrokes(strokes); } XFA_BOX_Fill(box, strokes, pGS, rtWidget, pMatrix, dwFlags); XFA_BOX_Stroke(box, strokes, pGS, rtWidget, pMatrix, dwFlags); }