| // 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/fxgraphics/cxfa_graphics.h" |
| |
| #include <memory> |
| |
| #include "core/fxge/cfx_defaultrenderdevice.h" |
| #include "core/fxge/cfx_renderdevice.h" |
| #include "core/fxge/cfx_unicodeencoding.h" |
| #include "third_party/base/ptr_util.h" |
| #include "xfa/fxgraphics/cxfa_color.h" |
| #include "xfa/fxgraphics/cxfa_path.h" |
| #include "xfa/fxgraphics/cxfa_pattern.h" |
| #include "xfa/fxgraphics/cxfa_shading.h" |
| |
| namespace { |
| |
| enum { |
| FX_CONTEXT_None = 0, |
| FX_CONTEXT_Device, |
| }; |
| |
| #define FX_HATCHSTYLE_Total 53 |
| |
| struct FX_HATCHDATA { |
| int32_t width; |
| int32_t height; |
| uint8_t maskBits[64]; |
| }; |
| |
| const FX_HATCHDATA hatchBitmapData[FX_HATCHSTYLE_Total] = { |
| {16, // Horizontal |
| 16, |
| { |
| 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, |
| 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }}, |
| {16, // Vertical |
| 16, |
| { |
| 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, |
| 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, |
| 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, |
| 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, |
| 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, |
| 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, |
| }}, |
| {16, // ForwardDiagonal |
| 16, |
| { |
| 0x80, 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, |
| 0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, |
| 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x80, |
| 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, |
| 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00, |
| 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, |
| }}, |
| {16, // BackwardDiagonal |
| 16, |
| { |
| 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, |
| 0x00, 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, |
| 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x01, |
| 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, |
| 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, 0x00, |
| 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, |
| }}, |
| {16, // Cross |
| 16, |
| { |
| 0xff, 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, |
| 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, |
| 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0xff, |
| 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, |
| 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, |
| 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, |
| }}, |
| {16, // DiagonalCross |
| 16, |
| { |
| 0x81, 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, |
| 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, |
| 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, |
| 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, |
| 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, 0x00, |
| 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, |
| }}, |
| }; |
| |
| } // namespace |
| |
| CXFA_Graphics::CXFA_Graphics(CFX_RenderDevice* renderDevice) |
| : m_type(FX_CONTEXT_None), m_renderDevice(renderDevice) { |
| if (!renderDevice) |
| return; |
| m_type = FX_CONTEXT_Device; |
| } |
| |
| CXFA_Graphics::~CXFA_Graphics() {} |
| |
| void CXFA_Graphics::SaveGraphState() { |
| if (m_type != FX_CONTEXT_Device || !m_renderDevice) |
| return; |
| |
| m_renderDevice->SaveState(); |
| m_infoStack.push_back(pdfium::MakeUnique<TInfo>(m_info)); |
| } |
| |
| void CXFA_Graphics::RestoreGraphState() { |
| if (m_type != FX_CONTEXT_Device || !m_renderDevice) |
| return; |
| |
| m_renderDevice->RestoreState(false); |
| if (m_infoStack.empty() || !m_infoStack.back()) |
| return; |
| |
| m_info = *m_infoStack.back(); |
| m_infoStack.pop_back(); |
| return; |
| } |
| |
| void CXFA_Graphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) { |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) { |
| m_info.graphState.m_LineCap = lineCap; |
| } |
| } |
| |
| void CXFA_Graphics::SetLineDash(float dashPhase, |
| float* dashArray, |
| int32_t dashCount) { |
| if (dashCount > 0 && !dashArray) |
| return; |
| |
| dashCount = dashCount < 0 ? 0 : dashCount; |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) { |
| float scale = 1.0; |
| if (m_info.isActOnDash) { |
| scale = m_info.graphState.m_LineWidth; |
| } |
| m_info.graphState.m_DashPhase = dashPhase; |
| m_info.graphState.SetDashCount(dashCount); |
| for (int32_t i = 0; i < dashCount; i++) { |
| m_info.graphState.m_DashArray[i] = dashArray[i] * scale; |
| } |
| } |
| } |
| |
| void CXFA_Graphics::SetLineDash(FX_DashStyle dashStyle) { |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) |
| RenderDeviceSetLineDash(dashStyle); |
| } |
| |
| void CXFA_Graphics::SetLineWidth(float lineWidth, bool isActOnDash) { |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) { |
| m_info.graphState.m_LineWidth = lineWidth; |
| m_info.isActOnDash = isActOnDash; |
| } |
| } |
| |
| void CXFA_Graphics::SetStrokeColor(CXFA_Color* color) { |
| if (!color) |
| return; |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) { |
| m_info.strokeColor = color; |
| } |
| } |
| |
| void CXFA_Graphics::SetFillColor(CXFA_Color* color) { |
| if (!color) |
| return; |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) { |
| m_info.fillColor = color; |
| } |
| } |
| |
| void CXFA_Graphics::StrokePath(CXFA_Path* path, CFX_Matrix* matrix) { |
| if (!path) |
| return; |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) |
| RenderDeviceStrokePath(path, matrix); |
| } |
| |
| void CXFA_Graphics::FillPath(CXFA_Path* path, |
| FX_FillMode fillMode, |
| CFX_Matrix* matrix) { |
| if (!path) |
| return; |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) |
| RenderDeviceFillPath(path, fillMode, matrix); |
| } |
| |
| void CXFA_Graphics::StretchImage(const CFX_RetainPtr<CFX_DIBSource>& source, |
| const CFX_RectF& rect, |
| CFX_Matrix* matrix) { |
| if (!source) |
| return; |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) |
| RenderDeviceStretchImage(source, rect, matrix); |
| } |
| |
| void CXFA_Graphics::ConcatMatrix(const CFX_Matrix* matrix) { |
| if (!matrix) |
| return; |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) { |
| m_info.CTM.Concat(*matrix); |
| } |
| } |
| |
| CFX_Matrix* CXFA_Graphics::GetMatrix() { |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) |
| return &m_info.CTM; |
| return nullptr; |
| } |
| |
| CFX_RectF CXFA_Graphics::GetClipRect() const { |
| if (m_type != FX_CONTEXT_Device || !m_renderDevice) |
| return CFX_RectF(); |
| |
| FX_RECT r = m_renderDevice->GetClipBox(); |
| return CFX_Rect(r.left, r.top, r.Width(), r.Height()).As<float>(); |
| } |
| |
| void CXFA_Graphics::SetClipRect(const CFX_RectF& rect) { |
| if (m_type == FX_CONTEXT_Device && m_renderDevice) { |
| m_renderDevice->SetClip_Rect( |
| FX_RECT(FXSYS_round(rect.left), FXSYS_round(rect.top), |
| FXSYS_round(rect.right()), FXSYS_round(rect.bottom()))); |
| } |
| } |
| |
| CFX_RenderDevice* CXFA_Graphics::GetRenderDevice() { |
| return m_renderDevice; |
| } |
| |
| void CXFA_Graphics::RenderDeviceSetLineDash(FX_DashStyle dashStyle) { |
| switch (dashStyle) { |
| case FX_DASHSTYLE_Solid: { |
| m_info.graphState.SetDashCount(0); |
| return; |
| } |
| case FX_DASHSTYLE_Dash: { |
| float dashArray[] = {3, 1}; |
| SetLineDash(0, dashArray, 2); |
| return; |
| } |
| case FX_DASHSTYLE_Dot: { |
| float dashArray[] = {1, 1}; |
| SetLineDash(0, dashArray, 2); |
| return; |
| } |
| case FX_DASHSTYLE_DashDot: { |
| float dashArray[] = {3, 1, 1, 1}; |
| SetLineDash(0, dashArray, 4); |
| return; |
| } |
| case FX_DASHSTYLE_DashDotDot: { |
| float dashArray[] = {4, 1, 2, 1, 2, 1}; |
| SetLineDash(0, dashArray, 6); |
| return; |
| } |
| default: |
| return; |
| } |
| } |
| |
| void CXFA_Graphics::RenderDeviceStrokePath(CXFA_Path* path, |
| CFX_Matrix* matrix) { |
| if (!m_info.strokeColor) |
| return; |
| CFX_Matrix m(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d, |
| m_info.CTM.e, m_info.CTM.f); |
| if (matrix) { |
| m.Concat(*matrix); |
| } |
| switch (m_info.strokeColor->m_type) { |
| case FX_COLOR_Solid: { |
| m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState, 0x0, |
| m_info.strokeColor->m_info.argb, 0); |
| return; |
| } |
| default: |
| return; |
| } |
| } |
| |
| void CXFA_Graphics::RenderDeviceFillPath(CXFA_Path* path, |
| FX_FillMode fillMode, |
| CFX_Matrix* matrix) { |
| if (!m_info.fillColor) |
| return; |
| CFX_Matrix m(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d, |
| m_info.CTM.e, m_info.CTM.f); |
| if (matrix) { |
| m.Concat(*matrix); |
| } |
| switch (m_info.fillColor->m_type) { |
| case FX_COLOR_Solid: { |
| m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState, |
| m_info.fillColor->m_info.argb, 0x0, fillMode); |
| return; |
| } |
| case FX_COLOR_Pattern: |
| FillPathWithPattern(path, fillMode, &m); |
| return; |
| case FX_COLOR_Shading: |
| FillPathWithShading(path, fillMode, &m); |
| return; |
| default: |
| return; |
| } |
| } |
| |
| void CXFA_Graphics::RenderDeviceStretchImage( |
| const CFX_RetainPtr<CFX_DIBSource>& source, |
| const CFX_RectF& rect, |
| CFX_Matrix* matrix) { |
| CFX_Matrix m1(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d, |
| m_info.CTM.e, m_info.CTM.f); |
| if (matrix) { |
| m1.Concat(*matrix); |
| } |
| CFX_RetainPtr<CFX_DIBitmap> bmp1 = |
| source->StretchTo(static_cast<int32_t>(rect.Width()), |
| static_cast<int32_t>(rect.Height()), 0, nullptr); |
| CFX_Matrix m2(rect.Width(), 0.0, 0.0, rect.Height(), rect.left, rect.top); |
| m2.Concat(m1); |
| |
| int32_t left; |
| int32_t top; |
| CFX_RetainPtr<CFX_DIBitmap> bmp2 = bmp1->FlipImage(false, true); |
| CFX_RetainPtr<CFX_DIBitmap> bmp3 = bmp2->TransformTo(&m2, &left, &top); |
| CFX_RectF r = GetClipRect(); |
| CFX_RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap(); |
| bitmap->CompositeBitmap(FXSYS_round(r.left), FXSYS_round(r.top), |
| FXSYS_round(r.Width()), FXSYS_round(r.Height()), bmp3, |
| FXSYS_round(r.left - left), FXSYS_round(r.top - top)); |
| } |
| |
| void CXFA_Graphics::FillPathWithPattern(CXFA_Path* path, |
| FX_FillMode fillMode, |
| CFX_Matrix* matrix) { |
| CXFA_Pattern* pattern = m_info.fillColor->m_info.pattern; |
| CFX_RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap(); |
| int32_t width = bitmap->GetWidth(); |
| int32_t height = bitmap->GetHeight(); |
| auto bmp = pdfium::MakeRetain<CFX_DIBitmap>(); |
| bmp->Create(width, height, FXDIB_Argb); |
| m_renderDevice->GetDIBits(bmp, 0, 0); |
| |
| FX_HatchStyle hatchStyle = m_info.fillColor->m_info.pattern->m_hatchStyle; |
| const FX_HATCHDATA& data = hatchBitmapData[static_cast<int>(hatchStyle)]; |
| |
| auto mask = pdfium::MakeRetain<CFX_DIBitmap>(); |
| mask->Create(data.width, data.height, FXDIB_1bppMask); |
| memcpy(mask->GetBuffer(), data.maskBits, mask->GetPitch() * data.height); |
| CFX_FloatRect rectf = path->GetPathData()->GetBoundingBox(); |
| if (matrix) |
| matrix->TransformRect(rectf); |
| |
| FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top), |
| FXSYS_round(rectf.right), FXSYS_round(rectf.bottom)); |
| CFX_DefaultRenderDevice device; |
| device.Attach(bmp, false, nullptr, false); |
| device.FillRect(&rect, m_info.fillColor->m_info.pattern->m_backArgb); |
| for (int32_t j = rect.bottom; j < rect.top; j += mask->GetHeight()) { |
| for (int32_t i = rect.left; i < rect.right; i += mask->GetWidth()) { |
| device.SetBitMask(mask, i, j, |
| m_info.fillColor->m_info.pattern->m_foreArgb); |
| } |
| } |
| CFX_RenderDevice::StateRestorer restorer(m_renderDevice); |
| m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode); |
| SetDIBitsWithMatrix(bmp, &pattern->m_matrix); |
| } |
| |
| void CXFA_Graphics::FillPathWithShading(CXFA_Path* path, |
| FX_FillMode fillMode, |
| CFX_Matrix* matrix) { |
| CFX_RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap(); |
| int32_t width = bitmap->GetWidth(); |
| int32_t height = bitmap->GetHeight(); |
| float start_x = m_info.fillColor->m_shading->m_beginPoint.x; |
| float start_y = m_info.fillColor->m_shading->m_beginPoint.y; |
| float end_x = m_info.fillColor->m_shading->m_endPoint.x; |
| float end_y = m_info.fillColor->m_shading->m_endPoint.y; |
| auto bmp = pdfium::MakeRetain<CFX_DIBitmap>(); |
| bmp->Create(width, height, FXDIB_Argb); |
| m_renderDevice->GetDIBits(bmp, 0, 0); |
| int32_t pitch = bmp->GetPitch(); |
| bool result = false; |
| switch (m_info.fillColor->m_shading->m_type) { |
| case FX_SHADING_Axial: { |
| float x_span = end_x - start_x; |
| float y_span = end_y - start_y; |
| float axis_len_square = (x_span * x_span) + (y_span * y_span); |
| for (int32_t row = 0; row < height; row++) { |
| uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch); |
| for (int32_t column = 0; column < width; column++) { |
| float x = (float)(column); |
| float y = (float)(row); |
| float scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) / |
| axis_len_square; |
| if (scale < 0) { |
| if (!m_info.fillColor->m_shading->m_isExtendedBegin) { |
| continue; |
| } |
| scale = 0; |
| } else if (scale > 1.0f) { |
| if (!m_info.fillColor->m_shading->m_isExtendedEnd) { |
| continue; |
| } |
| scale = 1.0f; |
| } |
| int32_t index = (int32_t)(scale * (FX_SHADING_Steps - 1)); |
| dib_buf[column] = m_info.fillColor->m_shading->m_argbArray[index]; |
| } |
| } |
| result = true; |
| break; |
| } |
| case FX_SHADING_Radial: { |
| float start_r = m_info.fillColor->m_shading->m_beginRadius; |
| float end_r = m_info.fillColor->m_shading->m_endRadius; |
| float a = ((start_x - end_x) * (start_x - end_x)) + |
| ((start_y - end_y) * (start_y - end_y)) - |
| ((start_r - end_r) * (start_r - end_r)); |
| for (int32_t row = 0; row < height; row++) { |
| uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch); |
| for (int32_t column = 0; column < width; column++) { |
| float x = (float)(column); |
| float y = (float)(row); |
| float b = -2 * (((x - start_x) * (end_x - start_x)) + |
| ((y - start_y) * (end_y - start_y)) + |
| (start_r * (end_r - start_r))); |
| float c = ((x - start_x) * (x - start_x)) + |
| ((y - start_y) * (y - start_y)) - (start_r * start_r); |
| float s; |
| if (a == 0) { |
| s = -c / b; |
| } else { |
| float b2_4ac = (b * b) - 4 * (a * c); |
| if (b2_4ac < 0) { |
| continue; |
| } |
| float root = (sqrt(b2_4ac)); |
| float s1, s2; |
| if (a > 0) { |
| s1 = (-b - root) / (2 * a); |
| s2 = (-b + root) / (2 * a); |
| } else { |
| s2 = (-b - root) / (2 * a); |
| s1 = (-b + root) / (2 * a); |
| } |
| if (s2 <= 1.0f || m_info.fillColor->m_shading->m_isExtendedEnd) { |
| s = (s2); |
| } else { |
| s = (s1); |
| } |
| if ((start_r) + s * (end_r - start_r) < 0) { |
| continue; |
| } |
| } |
| if (s < 0) { |
| if (!m_info.fillColor->m_shading->m_isExtendedBegin) { |
| continue; |
| } |
| s = 0; |
| } |
| if (s > 1.0f) { |
| if (!m_info.fillColor->m_shading->m_isExtendedEnd) { |
| continue; |
| } |
| s = 1.0f; |
| } |
| int index = (int32_t)(s * (FX_SHADING_Steps - 1)); |
| dib_buf[column] = m_info.fillColor->m_shading->m_argbArray[index]; |
| } |
| } |
| result = true; |
| break; |
| } |
| default: { |
| result = false; |
| break; |
| } |
| } |
| if (result) { |
| CFX_RenderDevice::StateRestorer restorer(m_renderDevice); |
| m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode); |
| SetDIBitsWithMatrix(bmp, matrix); |
| } |
| } |
| |
| void CXFA_Graphics::SetDIBitsWithMatrix( |
| const CFX_RetainPtr<CFX_DIBSource>& source, |
| CFX_Matrix* matrix) { |
| if (matrix->IsIdentity()) { |
| m_renderDevice->SetDIBits(source, 0, 0); |
| } else { |
| CFX_Matrix m((float)source->GetWidth(), 0, 0, (float)source->GetHeight(), 0, |
| 0); |
| m.Concat(*matrix); |
| int32_t left; |
| int32_t top; |
| CFX_RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true); |
| CFX_RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(&m, &left, &top); |
| m_renderDevice->SetDIBits(bmp2, left, top); |
| } |
| } |
| |
| CXFA_Graphics::TInfo::TInfo() |
| : isActOnDash(false), strokeColor(nullptr), fillColor(nullptr) {} |
| |
| CXFA_Graphics::TInfo::TInfo(const TInfo& info) |
| : graphState(info.graphState), |
| CTM(info.CTM), |
| isActOnDash(info.isActOnDash), |
| strokeColor(info.strokeColor), |
| fillColor(info.fillColor) {} |
| |
| CXFA_Graphics::TInfo& CXFA_Graphics::TInfo::operator=(const TInfo& other) { |
| graphState.Copy(other.graphState); |
| CTM = other.CTM; |
| isActOnDash = other.isActOnDash; |
| strokeColor = other.strokeColor; |
| fillColor = other.fillColor; |
| return *this; |
| } |