// 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 "core/fpdfapi/fpdf_page/include/cpdf_pageobject.h" #include "core/fpdfapi/fpdf_page/include/cpdf_pageobjectholder.h" #include "core/fpdfapi/fpdf_page/include/cpdf_pathobject.h" #include "core/fpdfapi/fpdf_page/include/cpdf_textobject.h" #include "core/fpdfapi/fpdf_render/include/cpdf_renderoptions.h" #include "core/fpdfapi/fpdf_render/include/cpdf_textrenderer.h" #include "core/fpdfdoc/include/cpvt_word.h" #include "core/fpdfdoc/include/ipvt_fontmap.h" #include "core/fxge/include/fx_ge.h" #include "fpdfsdk/cfx_systemhandler.h" #include "fpdfsdk/fxedit/include/fx_edit.h" #include "fpdfsdk/fxedit/include/fxet_edit.h" namespace { CFX_FloatRect GetUnderLineRect(const CPVT_Word& word) { return CFX_FloatRect(word.ptWord.x, word.ptWord.y + word.fDescent * 0.5f, word.ptWord.x + word.fWidth, word.ptWord.y + word.fDescent * 0.25f); } CFX_FloatRect GetCrossoutRect(const CPVT_Word& word) { return CFX_FloatRect(word.ptWord.x, word.ptWord.y + (word.fAscent + word.fDescent) * 0.5f + word.fDescent * 0.25f, word.ptWord.x + word.fWidth, word.ptWord.y + (word.fAscent + word.fDescent) * 0.5f); } void DrawTextString(CFX_RenderDevice* pDevice, const CFX_FloatPoint& pt, CPDF_Font* pFont, FX_FLOAT fFontSize, CFX_Matrix* pUser2Device, const CFX_ByteString& str, FX_ARGB crTextFill, FX_ARGB crTextStroke, int32_t nHorzScale) { FX_FLOAT x = pt.x, y = pt.y; pUser2Device->Transform(x, y); if (pFont) { if (nHorzScale != 100) { CFX_Matrix mt(nHorzScale / 100.0f, 0, 0, 1, 0, 0); mt.Concat(*pUser2Device); CPDF_RenderOptions ro; ro.m_Flags = RENDER_CLEARTYPE; ro.m_ColorMode = RENDER_COLOR_NORMAL; if (crTextStroke != 0) { CFX_FloatPoint pt1(0, 0), pt2(1, 0); pUser2Device->Transform(pt1.x, pt1.y); pUser2Device->Transform(pt2.x, pt2.y); CFX_GraphStateData gsd; gsd.m_LineWidth = (FX_FLOAT)FXSYS_fabs((pt2.x + pt2.y) - (pt1.x + pt1.y)); CPDF_TextRenderer::DrawTextString(pDevice, x, y, pFont, fFontSize, &mt, str, crTextFill, crTextStroke, &gsd, &ro); } else { CPDF_TextRenderer::DrawTextString(pDevice, x, y, pFont, fFontSize, &mt, str, crTextFill, 0, nullptr, &ro); } } else { CPDF_RenderOptions ro; ro.m_Flags = RENDER_CLEARTYPE; ro.m_ColorMode = RENDER_COLOR_NORMAL; if (crTextStroke != 0) { CFX_FloatPoint pt1(0, 0), pt2(1, 0); pUser2Device->Transform(pt1.x, pt1.y); pUser2Device->Transform(pt2.x, pt2.y); CFX_GraphStateData gsd; gsd.m_LineWidth = (FX_FLOAT)FXSYS_fabs((pt2.x + pt2.y) - (pt1.x + pt1.y)); CPDF_TextRenderer::DrawTextString(pDevice, x, y, pFont, fFontSize, pUser2Device, str, crTextFill, crTextStroke, &gsd, &ro); } else { CPDF_TextRenderer::DrawTextString(pDevice, x, y, pFont, fFontSize, pUser2Device, str, crTextFill, 0, nullptr, &ro); } } } } void AddRectToPageObjects(CPDF_PageObjectHolder* pObjectHolder, FX_COLORREF crFill, const CFX_FloatRect& rcFill) { std::unique_ptr<CPDF_PathObject> pPathObj(new CPDF_PathObject); CFX_PathData* pPathData = pPathObj->m_Path.GetModify(); pPathData->AppendRect(rcFill.left, rcFill.bottom, rcFill.right, rcFill.top); FX_FLOAT rgb[3]; rgb[0] = FXARGB_R(crFill) / 255.0f; rgb[1] = FXARGB_G(crFill) / 255.0f; rgb[2] = FXARGB_B(crFill) / 255.0f; pPathObj->m_ColorState.SetFillColor( CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3); pPathObj->m_FillType = FXFILL_ALTERNATE; pPathObj->m_bStroke = FALSE; pObjectHolder->GetPageObjectList()->push_back(std::move(pPathObj)); } CPDF_TextObject* AddTextObjToPageObjects(CPDF_PageObjectHolder* pObjectHolder, FX_COLORREF crText, CPDF_Font* pFont, FX_FLOAT fFontSize, FX_FLOAT fCharSpace, int32_t nHorzScale, const CFX_FloatPoint& point, const CFX_ByteString& text) { std::unique_ptr<CPDF_TextObject> pTxtObj(new CPDF_TextObject); CPDF_TextStateData* pTextStateData = pTxtObj->m_TextState.GetModify(); pTextStateData->m_pFont = pFont; pTextStateData->m_FontSize = fFontSize; pTextStateData->m_CharSpace = fCharSpace; pTextStateData->m_WordSpace = 0; pTextStateData->m_TextMode = TextRenderingMode::MODE_FILL; pTextStateData->m_Matrix[0] = nHorzScale / 100.0f; pTextStateData->m_Matrix[1] = 0; pTextStateData->m_Matrix[2] = 0; pTextStateData->m_Matrix[3] = 1; FX_FLOAT rgb[3]; rgb[0] = FXARGB_R(crText) / 255.0f; rgb[1] = FXARGB_G(crText) / 255.0f; rgb[2] = FXARGB_B(crText) / 255.0f; pTxtObj->m_ColorState.SetFillColor( CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3); pTxtObj->m_ColorState.SetStrokeColor( CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3); pTxtObj->SetPosition(point.x, point.y); pTxtObj->SetText(text); CPDF_TextObject* pRet = pTxtObj.get(); pObjectHolder->GetPageObjectList()->push_back(std::move(pTxtObj)); return pRet; } } // namespace void IFX_Edit::DrawUnderline(CFX_RenderDevice* pDevice, CFX_Matrix* pUser2Device, IFX_Edit* pEdit, FX_COLORREF color, const CFX_FloatRect& rcClip, const CFX_FloatPoint& ptOffset, const CPVT_WordRange* pRange) { pDevice->SaveState(); if (!rcClip.IsEmpty()) { CFX_FloatRect rcTemp = rcClip; pUser2Device->TransformRect(rcTemp); pDevice->SetClip_Rect(rcTemp.ToFxRect()); } IFX_Edit_Iterator* pIterator = pEdit->GetIterator(); if (pEdit->GetFontMap()) { if (pRange) pIterator->SetAt(pRange->BeginPos); else pIterator->SetAt(0); while (pIterator->NextWord()) { CPVT_WordPlace place = pIterator->GetAt(); if (pRange && place.WordCmp(pRange->EndPos) > 0) break; CPVT_Word word; if (pIterator->GetWord(word)) { CFX_PathData pathUnderline; CFX_FloatRect rcUnderline = GetUnderLineRect(word); rcUnderline.left += ptOffset.x; rcUnderline.right += ptOffset.x; rcUnderline.top += ptOffset.y; rcUnderline.bottom += ptOffset.y; pathUnderline.AppendRect(rcUnderline.left, rcUnderline.bottom, rcUnderline.right, rcUnderline.top); pDevice->DrawPath(&pathUnderline, pUser2Device, nullptr, color, 0, FXFILL_WINDING); } } } pDevice->RestoreState(false); } void IFX_Edit::DrawEdit(CFX_RenderDevice* pDevice, CFX_Matrix* pUser2Device, IFX_Edit* pEdit, FX_COLORREF crTextFill, FX_COLORREF crTextStroke, const CFX_FloatRect& rcClip, const CFX_FloatPoint& ptOffset, const CPVT_WordRange* pRange, CFX_SystemHandler* pSystemHandler, void* pFFLData) { const bool bContinuous = pEdit->GetCharArray() == 0 && pEdit->GetCharSpace() <= 0.0f; uint16_t SubWord = pEdit->GetPasswordChar(); FX_FLOAT fFontSize = pEdit->GetFontSize(); CPVT_WordRange wrSelect = pEdit->GetSelectWordRange(); int32_t nHorzScale = pEdit->GetHorzScale(); FX_COLORREF crCurFill = crTextFill; FX_COLORREF crOldFill = crCurFill; FX_BOOL bSelect = FALSE; const FX_COLORREF crWhite = ArgbEncode(255, 255, 255, 255); const FX_COLORREF crSelBK = ArgbEncode(255, 0, 51, 113); CFX_ByteTextBuf sTextBuf; int32_t nFontIndex = -1; CFX_FloatPoint ptBT(0.0f, 0.0f); pDevice->SaveState(); if (!rcClip.IsEmpty()) { CFX_FloatRect rcTemp = rcClip; pUser2Device->TransformRect(rcTemp); pDevice->SetClip_Rect(rcTemp.ToFxRect()); } IFX_Edit_Iterator* pIterator = pEdit->GetIterator(); if (IPVT_FontMap* pFontMap = pEdit->GetFontMap()) { if (pRange) pIterator->SetAt(pRange->BeginPos); else pIterator->SetAt(0); CPVT_WordPlace oldplace; while (pIterator->NextWord()) { CPVT_WordPlace place = pIterator->GetAt(); if (pRange && place.WordCmp(pRange->EndPos) > 0) break; if (wrSelect.IsExist()) { bSelect = place.WordCmp(wrSelect.BeginPos) > 0 && place.WordCmp(wrSelect.EndPos) <= 0; crCurFill = bSelect ? crWhite : crTextFill; } if (pSystemHandler && pSystemHandler->IsSelectionImplemented()) { crCurFill = crTextFill; crOldFill = crCurFill; } CPVT_Word word; if (pIterator->GetWord(word)) { if (bSelect) { CPVT_Line line; pIterator->GetLine(line); if (pSystemHandler && pSystemHandler->IsSelectionImplemented()) { CFX_FloatRect rc(word.ptWord.x, line.ptLine.y + line.fLineDescent, word.ptWord.x + word.fWidth, line.ptLine.y + line.fLineAscent); rc.Intersect(rcClip); pSystemHandler->OutputSelectedRect(pFFLData, rc); } else { CFX_PathData pathSelBK; pathSelBK.AppendRect( word.ptWord.x, line.ptLine.y + line.fLineDescent, word.ptWord.x + word.fWidth, line.ptLine.y + line.fLineAscent); pDevice->DrawPath(&pathSelBK, pUser2Device, nullptr, crSelBK, 0, FXFILL_WINDING); } } if (bContinuous) { if (place.LineCmp(oldplace) != 0 || word.nFontIndex != nFontIndex || crOldFill != crCurFill) { if (sTextBuf.GetLength() > 0) { DrawTextString( pDevice, CFX_FloatPoint(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), pFontMap->GetPDFFont(nFontIndex), fFontSize, pUser2Device, sTextBuf.MakeString(), crOldFill, crTextStroke, nHorzScale); sTextBuf.Clear(); } nFontIndex = word.nFontIndex; ptBT = word.ptWord; crOldFill = crCurFill; } sTextBuf << GetPDFWordString(pFontMap, word.nFontIndex, word.Word, SubWord) .AsStringC(); } else { DrawTextString( pDevice, CFX_FloatPoint(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y), pFontMap->GetPDFFont(word.nFontIndex), fFontSize, pUser2Device, GetPDFWordString(pFontMap, word.nFontIndex, word.Word, SubWord), crCurFill, crTextStroke, nHorzScale); } oldplace = place; } } if (sTextBuf.GetLength() > 0) { DrawTextString( pDevice, CFX_FloatPoint(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), pFontMap->GetPDFFont(nFontIndex), fFontSize, pUser2Device, sTextBuf.MakeString(), crOldFill, crTextStroke, nHorzScale); } } pDevice->RestoreState(false); } void IFX_Edit::DrawRichEdit(CFX_RenderDevice* pDevice, CFX_Matrix* pUser2Device, IFX_Edit* pEdit, const CFX_FloatRect& rcClip, const CFX_FloatPoint& ptOffset, const CPVT_WordRange* pRange) { CPVT_WordRange wrSelect = pEdit->GetSelectWordRange(); FX_COLORREF crCurText = ArgbEncode(255, 0, 0, 0); FX_COLORREF crOld = crCurText; FX_BOOL bSelect = FALSE; const FX_COLORREF crWhite = ArgbEncode(255, 255, 255, 255); const FX_COLORREF crSelBK = ArgbEncode(255, 0, 51, 113); CFX_ByteTextBuf sTextBuf; CPVT_WordProps wp; CFX_FloatPoint ptBT(0.0f, 0.0f); pDevice->SaveState(); if (!rcClip.IsEmpty()) { CFX_FloatRect rcTemp = rcClip; pUser2Device->TransformRect(rcTemp); pDevice->SetClip_Rect(rcTemp.ToFxRect()); } IFX_Edit_Iterator* pIterator = pEdit->GetIterator(); if (IPVT_FontMap* pFontMap = pEdit->GetFontMap()) { if (pRange) pIterator->SetAt(pRange->BeginPos); else pIterator->SetAt(0); CPVT_WordPlace oldplace; while (pIterator->NextWord()) { CPVT_WordPlace place = pIterator->GetAt(); if (pRange && place.WordCmp(pRange->EndPos) > 0) break; CPVT_Word word; if (pIterator->GetWord(word)) { word.WordProps.fFontSize = word.fFontSize; crCurText = ArgbEncode(255, word.WordProps.dwWordColor); if (wrSelect.IsExist()) { bSelect = place.WordCmp(wrSelect.BeginPos) > 0 && place.WordCmp(wrSelect.EndPos) <= 0; if (bSelect) { crCurText = crWhite; } } if (bSelect) { CPVT_Line line; pIterator->GetLine(line); CFX_PathData pathSelBK; pathSelBK.AppendRect(word.ptWord.x + ptOffset.x, line.ptLine.y + line.fLineDescent + ptOffset.y, word.ptWord.x + word.fWidth + ptOffset.x, line.ptLine.y + line.fLineAscent + ptOffset.y); pDevice->DrawPath(&pathSelBK, pUser2Device, nullptr, crSelBK, 0, FXFILL_WINDING); } if (place.LineCmp(oldplace) != 0 || word.WordProps.fCharSpace > 0.0f || word.WordProps.nHorzScale != 100 || FXSYS_memcmp(&word.WordProps, &wp, sizeof(CPVT_WordProps)) != 0 || crOld != crCurText) { if (sTextBuf.GetLength() > 0) { DrawTextString( pDevice, CFX_FloatPoint(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), pFontMap->GetPDFFont(wp.nFontIndex), wp.fFontSize, pUser2Device, sTextBuf.MakeString(), crOld, 0, wp.nHorzScale); sTextBuf.Clear(); } wp = word.WordProps; ptBT = word.ptWord; crOld = crCurText; } sTextBuf << GetPDFWordString(pFontMap, word.WordProps.nFontIndex, word.Word, 0) .AsStringC(); if (word.WordProps.nWordStyle & PVTWORD_STYLE_UNDERLINE) { CFX_PathData pathUnderline; CFX_FloatRect rcUnderline = GetUnderLineRect(word); pathUnderline.AppendRect(rcUnderline.left, rcUnderline.bottom, rcUnderline.right, rcUnderline.top); pDevice->DrawPath(&pathUnderline, pUser2Device, nullptr, crCurText, 0, FXFILL_WINDING); } if (word.WordProps.nWordStyle & PVTWORD_STYLE_CROSSOUT) { CFX_PathData pathCrossout; CFX_FloatRect rcCrossout = GetCrossoutRect(word); pathCrossout.AppendRect(rcCrossout.left, rcCrossout.bottom, rcCrossout.right, rcCrossout.top); pDevice->DrawPath(&pathCrossout, pUser2Device, nullptr, crCurText, 0, FXFILL_WINDING); } oldplace = place; } } if (sTextBuf.GetLength() > 0) { DrawTextString( pDevice, CFX_FloatPoint(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), pFontMap->GetPDFFont(wp.nFontIndex), wp.fFontSize, pUser2Device, sTextBuf.MakeString(), crOld, 0, wp.nHorzScale); } } pDevice->RestoreState(false); } void IFX_Edit::GeneratePageObjects( CPDF_PageObjectHolder* pObjectHolder, IFX_Edit* pEdit, const CFX_FloatPoint& ptOffset, const CPVT_WordRange* pRange, FX_COLORREF crText, CFX_ArrayTemplate<CPDF_TextObject*>& ObjArray) { FX_FLOAT fFontSize = pEdit->GetFontSize(); int32_t nOldFontIndex = -1; CFX_ByteTextBuf sTextBuf; CFX_FloatPoint ptBT(0.0f, 0.0f); ObjArray.RemoveAll(); IFX_Edit_Iterator* pIterator = pEdit->GetIterator(); if (IPVT_FontMap* pFontMap = pEdit->GetFontMap()) { if (pRange) pIterator->SetAt(pRange->BeginPos); else pIterator->SetAt(0); CPVT_WordPlace oldplace; while (pIterator->NextWord()) { CPVT_WordPlace place = pIterator->GetAt(); if (pRange && place.WordCmp(pRange->EndPos) > 0) break; CPVT_Word word; if (pIterator->GetWord(word)) { if (place.LineCmp(oldplace) != 0 || nOldFontIndex != word.nFontIndex) { if (sTextBuf.GetLength() > 0) { ObjArray.Add(AddTextObjToPageObjects( pObjectHolder, crText, pFontMap->GetPDFFont(nOldFontIndex), fFontSize, 0.0f, 100, CFX_FloatPoint(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), sTextBuf.MakeString())); sTextBuf.Clear(); } ptBT = word.ptWord; nOldFontIndex = word.nFontIndex; } sTextBuf << GetPDFWordString(pFontMap, word.nFontIndex, word.Word, 0) .AsStringC(); oldplace = place; } } if (sTextBuf.GetLength() > 0) { ObjArray.Add(AddTextObjToPageObjects( pObjectHolder, crText, pFontMap->GetPDFFont(nOldFontIndex), fFontSize, 0.0f, 100, CFX_FloatPoint(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), sTextBuf.MakeString())); } } } void IFX_Edit::GenerateRichPageObjects( CPDF_PageObjectHolder* pObjectHolder, IFX_Edit* pEdit, const CFX_FloatPoint& ptOffset, const CPVT_WordRange* pRange, CFX_ArrayTemplate<CPDF_TextObject*>& ObjArray) { FX_COLORREF crCurText = ArgbEncode(255, 0, 0, 0); FX_COLORREF crOld = crCurText; CFX_ByteTextBuf sTextBuf; CPVT_WordProps wp; CFX_FloatPoint ptBT(0.0f, 0.0f); ObjArray.RemoveAll(); IFX_Edit_Iterator* pIterator = pEdit->GetIterator(); if (IPVT_FontMap* pFontMap = pEdit->GetFontMap()) { if (pRange) pIterator->SetAt(pRange->BeginPos); else pIterator->SetAt(0); CPVT_WordPlace oldplace; while (pIterator->NextWord()) { CPVT_WordPlace place = pIterator->GetAt(); if (pRange && place.WordCmp(pRange->EndPos) > 0) break; CPVT_Word word; if (pIterator->GetWord(word)) { word.WordProps.fFontSize = word.fFontSize; crCurText = ArgbEncode(255, word.WordProps.dwWordColor); if (place.LineCmp(oldplace) != 0 || word.WordProps.fCharSpace > 0.0f || word.WordProps.nHorzScale != 100 || FXSYS_memcmp(&word.WordProps, &wp, sizeof(CPVT_WordProps)) != 0 || crOld != crCurText) { if (sTextBuf.GetLength() > 0) { ObjArray.Add(AddTextObjToPageObjects( pObjectHolder, crOld, pFontMap->GetPDFFont(wp.nFontIndex), wp.fFontSize, wp.fCharSpace, wp.nHorzScale, CFX_FloatPoint(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), sTextBuf.MakeString())); sTextBuf.Clear(); } wp = word.WordProps; ptBT = word.ptWord; crOld = crCurText; } sTextBuf << GetPDFWordString(pFontMap, word.WordProps.nFontIndex, word.Word, 0) .AsStringC(); if (word.WordProps.nWordStyle & PVTWORD_STYLE_UNDERLINE) { CFX_FloatRect rcUnderline = GetUnderLineRect(word); rcUnderline.left += ptOffset.x; rcUnderline.right += ptOffset.x; rcUnderline.top += ptOffset.y; rcUnderline.bottom += ptOffset.y; AddRectToPageObjects(pObjectHolder, crCurText, rcUnderline); } if (word.WordProps.nWordStyle & PVTWORD_STYLE_CROSSOUT) { CFX_FloatRect rcCrossout = GetCrossoutRect(word); rcCrossout.left += ptOffset.x; rcCrossout.right += ptOffset.x; rcCrossout.top += ptOffset.y; rcCrossout.bottom += ptOffset.y; AddRectToPageObjects(pObjectHolder, crCurText, rcCrossout); } oldplace = place; } } if (sTextBuf.GetLength() > 0) { ObjArray.Add(AddTextObjToPageObjects( pObjectHolder, crOld, pFontMap->GetPDFFont(wp.nFontIndex), wp.fFontSize, wp.fCharSpace, wp.nHorzScale, CFX_FloatPoint(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), sTextBuf.MakeString())); } } } void IFX_Edit::GenerateUnderlineObjects(CPDF_PageObjectHolder* pObjectHolder, IFX_Edit* pEdit, const CFX_FloatPoint& ptOffset, const CPVT_WordRange* pRange, FX_COLORREF color) { IFX_Edit_Iterator* pIterator = pEdit->GetIterator(); if (pEdit->GetFontMap()) { if (pRange) pIterator->SetAt(pRange->BeginPos); else pIterator->SetAt(0); CPVT_WordPlace oldplace; while (pIterator->NextWord()) { CPVT_WordPlace place = pIterator->GetAt(); if (pRange && place.WordCmp(pRange->EndPos) > 0) break; CPVT_Word word; if (pIterator->GetWord(word)) { CFX_FloatRect rcUnderline = GetUnderLineRect(word); rcUnderline.left += ptOffset.x; rcUnderline.right += ptOffset.x; rcUnderline.top += ptOffset.y; rcUnderline.bottom += ptOffset.y; AddRectToPageObjects(pObjectHolder, color, rcUnderline); } } } }