1// Copyright 2014 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "core/include/fpdfapi/fpdf_pageobj.h"
8#include "core/include/fpdfapi/fpdf_render.h"
9#include "fpdfsdk/include/fx_systemhandler.h"
10#include "fpdfsdk/include/fxedit/fx_edit.h"
11#include "fpdfsdk/include/fxedit/fxet_edit.h"
12
13#define FX_EDIT_UNDERLINEHALFWIDTH 0.5f
14#define FX_EDIT_CROSSOUTHALFWIDTH 0.5f
15
16CPDF_Rect GetUnderLineRect(const CPVT_Word& word) {
17  return CPDF_Rect(word.ptWord.x, word.ptWord.y + word.fDescent * 0.5f,
18                   word.ptWord.x + word.fWidth,
19                   word.ptWord.y + word.fDescent * 0.25f);
20}
21
22CPDF_Rect GetCrossoutRect(const CPVT_Word& word) {
23  return CPDF_Rect(word.ptWord.x,
24                   word.ptWord.y + (word.fAscent + word.fDescent) * 0.5f +
25                       word.fDescent * 0.25f,
26                   word.ptWord.x + word.fWidth,
27                   word.ptWord.y + (word.fAscent + word.fDescent) * 0.5f);
28}
29
30static void DrawTextString(CFX_RenderDevice* pDevice,
31                           const CPDF_Point& pt,
32                           CPDF_Font* pFont,
33                           FX_FLOAT fFontSize,
34                           CFX_Matrix* pUser2Device,
35                           const CFX_ByteString& str,
36                           FX_ARGB crTextFill,
37                           FX_ARGB crTextStroke,
38                           int32_t nHorzScale) {
39  FX_FLOAT x = pt.x, y = pt.y;
40  pUser2Device->Transform(x, y);
41
42  if (pFont) {
43    if (nHorzScale != 100) {
44      CFX_Matrix mt(nHorzScale / 100.0f, 0, 0, 1, 0, 0);
45      mt.Concat(*pUser2Device);
46
47      CPDF_RenderOptions ro;
48      ro.m_Flags = RENDER_CLEARTYPE;
49      ro.m_ColorMode = RENDER_COLOR_NORMAL;
50
51      if (crTextStroke != 0) {
52        CPDF_Point pt1(0, 0), pt2(1, 0);
53        pUser2Device->Transform(pt1.x, pt1.y);
54        pUser2Device->Transform(pt2.x, pt2.y);
55        CFX_GraphStateData gsd;
56        gsd.m_LineWidth =
57            (FX_FLOAT)FXSYS_fabs((pt2.x + pt2.y) - (pt1.x + pt1.y));
58
59        CPDF_TextRenderer::DrawTextString(pDevice, x, y, pFont, fFontSize, &mt,
60                                          str, crTextFill, crTextStroke, &gsd,
61                                          &ro);
62      } else {
63        CPDF_TextRenderer::DrawTextString(pDevice, x, y, pFont, fFontSize, &mt,
64                                          str, crTextFill, 0, NULL, &ro);
65      }
66    } else {
67      CPDF_RenderOptions ro;
68      ro.m_Flags = RENDER_CLEARTYPE;
69      ro.m_ColorMode = RENDER_COLOR_NORMAL;
70
71      if (crTextStroke != 0) {
72        CPDF_Point pt1(0, 0), pt2(1, 0);
73        pUser2Device->Transform(pt1.x, pt1.y);
74        pUser2Device->Transform(pt2.x, pt2.y);
75        CFX_GraphStateData gsd;
76        gsd.m_LineWidth =
77            (FX_FLOAT)FXSYS_fabs((pt2.x + pt2.y) - (pt1.x + pt1.y));
78
79        CPDF_TextRenderer::DrawTextString(pDevice, x, y, pFont, fFontSize,
80                                          pUser2Device, str, crTextFill,
81                                          crTextStroke, &gsd, &ro);
82      } else {
83        CPDF_TextRenderer::DrawTextString(pDevice, x, y, pFont, fFontSize,
84                                          pUser2Device, str, crTextFill, 0,
85                                          NULL, &ro);
86      }
87    }
88  }
89}
90
91void IFX_Edit::DrawUnderline(CFX_RenderDevice* pDevice,
92                             CFX_Matrix* pUser2Device,
93                             IFX_Edit* pEdit,
94                             FX_COLORREF color,
95                             const CPDF_Rect& rcClip,
96                             const CPDF_Point& ptOffset,
97                             const CPVT_WordRange* pRange) {
98  pDevice->SaveState();
99
100  if (!rcClip.IsEmpty()) {
101    CPDF_Rect rcTemp = rcClip;
102    pUser2Device->TransformRect(rcTemp);
103    FX_RECT rcDevClip;
104    rcDevClip.left = (int32_t)rcTemp.left;
105    rcDevClip.right = (int32_t)rcTemp.right;
106    rcDevClip.top = (int32_t)rcTemp.top;
107    rcDevClip.bottom = (int32_t)rcTemp.bottom;
108    pDevice->SetClip_Rect(&rcDevClip);
109  }
110
111  if (IFX_Edit_Iterator* pIterator = pEdit->GetIterator()) {
112    if (pEdit->GetFontMap()) {
113      if (pRange)
114        pIterator->SetAt(pRange->BeginPos);
115      else
116        pIterator->SetAt(0);
117
118      while (pIterator->NextWord()) {
119        CPVT_WordPlace place = pIterator->GetAt();
120        if (pRange && place.WordCmp(pRange->EndPos) > 0)
121          break;
122
123        CPVT_Word word;
124        if (pIterator->GetWord(word)) {
125          CFX_PathData pathUnderline;
126          CPDF_Rect rcUnderline = GetUnderLineRect(word);
127          rcUnderline.left += ptOffset.x;
128          rcUnderline.right += ptOffset.x;
129          rcUnderline.top += ptOffset.y;
130          rcUnderline.bottom += ptOffset.y;
131          pathUnderline.AppendRect(rcUnderline.left, rcUnderline.bottom,
132                                   rcUnderline.right, rcUnderline.top);
133
134          pDevice->DrawPath(&pathUnderline, pUser2Device, NULL, color, 0,
135                            FXFILL_WINDING);
136        }
137      }
138    }
139  }
140
141  pDevice->RestoreState();
142}
143
144void IFX_Edit::DrawEdit(CFX_RenderDevice* pDevice,
145                        CFX_Matrix* pUser2Device,
146                        IFX_Edit* pEdit,
147                        FX_COLORREF crTextFill,
148                        FX_COLORREF crTextStroke,
149                        const CPDF_Rect& rcClip,
150                        const CPDF_Point& ptOffset,
151                        const CPVT_WordRange* pRange,
152                        IFX_SystemHandler* pSystemHandler,
153                        void* pFFLData) {
154  FX_BOOL bContinuous = pEdit->GetCharArray() == 0;
155  if (pEdit->GetCharSpace() > 0.0f)
156    bContinuous = FALSE;
157
158  FX_WORD SubWord = pEdit->GetPasswordChar();
159  FX_FLOAT fFontSize = pEdit->GetFontSize();
160  CPVT_WordRange wrSelect = pEdit->GetSelectWordRange();
161  int32_t nHorzScale = pEdit->GetHorzScale();
162
163  FX_COLORREF crCurFill = crTextFill;
164  FX_COLORREF crOldFill = crCurFill;
165
166  FX_BOOL bSelect = FALSE;
167  const FX_COLORREF crWhite = ArgbEncode(255, 255, 255, 255);
168  const FX_COLORREF crSelBK = ArgbEncode(255, 0, 51, 113);
169
170  CFX_ByteTextBuf sTextBuf;
171  int32_t nFontIndex = -1;
172  CPDF_Point ptBT(0.0f, 0.0f);
173
174  pDevice->SaveState();
175
176  if (!rcClip.IsEmpty()) {
177    CPDF_Rect rcTemp = rcClip;
178    pUser2Device->TransformRect(rcTemp);
179    FX_RECT rcDevClip;
180    rcDevClip.left = (int32_t)rcTemp.left;
181    rcDevClip.right = (int32_t)rcTemp.right;
182    rcDevClip.top = (int32_t)rcTemp.top;
183    rcDevClip.bottom = (int32_t)rcTemp.bottom;
184    pDevice->SetClip_Rect(&rcDevClip);
185  }
186
187  if (IFX_Edit_Iterator* pIterator = pEdit->GetIterator()) {
188    if (IFX_Edit_FontMap* pFontMap = pEdit->GetFontMap()) {
189      if (pRange)
190        pIterator->SetAt(pRange->BeginPos);
191      else
192        pIterator->SetAt(0);
193
194      CPVT_WordPlace oldplace;
195
196      while (pIterator->NextWord()) {
197        CPVT_WordPlace place = pIterator->GetAt();
198        if (pRange && place.WordCmp(pRange->EndPos) > 0)
199          break;
200
201        if (wrSelect.IsExist()) {
202          bSelect = place.WordCmp(wrSelect.BeginPos) > 0 &&
203                    place.WordCmp(wrSelect.EndPos) <= 0;
204          if (bSelect) {
205            crCurFill = crWhite;
206          } else {
207            crCurFill = crTextFill;
208          }
209        }
210        if (pSystemHandler && pSystemHandler->IsSelectionImplemented()) {
211          crCurFill = crTextFill;
212          crOldFill = crCurFill;
213        }
214        CPVT_Word word;
215        if (pIterator->GetWord(word)) {
216          if (bSelect) {
217            CPVT_Line line;
218            pIterator->GetLine(line);
219
220            if (pSystemHandler && pSystemHandler->IsSelectionImplemented()) {
221              CPDF_Rect rc(word.ptWord.x, line.ptLine.y + line.fLineDescent,
222                           word.ptWord.x + word.fWidth,
223                           line.ptLine.y + line.fLineAscent);
224              rc.Intersect(rcClip);
225              pSystemHandler->OutputSelectedRect(pFFLData, rc);
226            } else {
227              CFX_PathData pathSelBK;
228              pathSelBK.AppendRect(word.ptWord.x,
229                                   line.ptLine.y + line.fLineDescent,
230                                   word.ptWord.x + word.fWidth,
231                                   line.ptLine.y + line.fLineAscent);
232
233              pDevice->DrawPath(&pathSelBK, pUser2Device, NULL, crSelBK, 0,
234                                FXFILL_WINDING);
235            }
236          }
237
238          if (bContinuous) {
239            if (place.LineCmp(oldplace) != 0 || word.nFontIndex != nFontIndex ||
240                crOldFill != crCurFill) {
241              if (sTextBuf.GetLength() > 0) {
242                DrawTextString(pDevice, CPDF_Point(ptBT.x + ptOffset.x,
243                                                   ptBT.y + ptOffset.y),
244                               pFontMap->GetPDFFont(nFontIndex), fFontSize,
245                               pUser2Device, sTextBuf.GetByteString(),
246                               crOldFill, crTextStroke, nHorzScale);
247
248                sTextBuf.Clear();
249              }
250              nFontIndex = word.nFontIndex;
251              ptBT = word.ptWord;
252              crOldFill = crCurFill;
253            }
254
255            sTextBuf << GetPDFWordString(pFontMap, word.nFontIndex, word.Word,
256                                         SubWord);
257          } else {
258            DrawTextString(
259                pDevice, CPDF_Point(word.ptWord.x + ptOffset.x,
260                                    word.ptWord.y + ptOffset.y),
261                pFontMap->GetPDFFont(word.nFontIndex), fFontSize, pUser2Device,
262                GetPDFWordString(pFontMap, word.nFontIndex, word.Word, SubWord),
263                crCurFill, crTextStroke, nHorzScale);
264          }
265          oldplace = place;
266        }
267      }
268
269      if (sTextBuf.GetLength() > 0) {
270        DrawTextString(
271            pDevice, CPDF_Point(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
272            pFontMap->GetPDFFont(nFontIndex), fFontSize, pUser2Device,
273            sTextBuf.GetByteString(), crOldFill, crTextStroke, nHorzScale);
274      }
275    }
276  }
277
278  pDevice->RestoreState();
279}
280
281void IFX_Edit::DrawRichEdit(CFX_RenderDevice* pDevice,
282                            CFX_Matrix* pUser2Device,
283                            IFX_Edit* pEdit,
284                            const CPDF_Rect& rcClip,
285                            const CPDF_Point& ptOffset,
286                            const CPVT_WordRange* pRange) {
287  CPVT_WordRange wrSelect = pEdit->GetSelectWordRange();
288
289  FX_COLORREF crCurText = ArgbEncode(255, 0, 0, 0);
290  FX_COLORREF crOld = crCurText;
291  FX_BOOL bSelect = FALSE;
292  const FX_COLORREF crWhite = ArgbEncode(255, 255, 255, 255);
293  const FX_COLORREF crSelBK = ArgbEncode(255, 0, 51, 113);
294
295  CFX_ByteTextBuf sTextBuf;
296  CPVT_WordProps wp;
297  CPDF_Point ptBT(0.0f, 0.0f);
298
299  pDevice->SaveState();
300
301  if (!rcClip.IsEmpty()) {
302    CPDF_Rect rcTemp = rcClip;
303    pUser2Device->TransformRect(rcTemp);
304    FX_RECT rcDevClip;
305    rcDevClip.left = (int32_t)rcTemp.left;
306    rcDevClip.right = (int32_t)rcTemp.right;
307    rcDevClip.top = (int32_t)rcTemp.top;
308    rcDevClip.bottom = (int32_t)rcTemp.bottom;
309    pDevice->SetClip_Rect(&rcDevClip);
310  }
311
312  if (IFX_Edit_Iterator* pIterator = pEdit->GetIterator()) {
313    if (IFX_Edit_FontMap* pFontMap = pEdit->GetFontMap()) {
314      if (pRange)
315        pIterator->SetAt(pRange->BeginPos);
316      else
317        pIterator->SetAt(0);
318
319      CPVT_WordPlace oldplace;
320
321      while (pIterator->NextWord()) {
322        CPVT_WordPlace place = pIterator->GetAt();
323        if (pRange && place.WordCmp(pRange->EndPos) > 0)
324          break;
325
326        CPVT_Word word;
327        if (pIterator->GetWord(word)) {
328          word.WordProps.fFontSize = word.fFontSize;
329
330          crCurText = ArgbEncode(255, word.WordProps.dwWordColor);
331
332          if (wrSelect.IsExist()) {
333            bSelect = place.WordCmp(wrSelect.BeginPos) > 0 &&
334                      place.WordCmp(wrSelect.EndPos) <= 0;
335            if (bSelect) {
336              crCurText = crWhite;
337            }
338          }
339
340          if (bSelect) {
341            CPVT_Line line;
342            pIterator->GetLine(line);
343
344            CFX_PathData pathSelBK;
345            pathSelBK.AppendRect(word.ptWord.x + ptOffset.x,
346                                 line.ptLine.y + line.fLineDescent + ptOffset.y,
347                                 word.ptWord.x + word.fWidth + ptOffset.x,
348                                 line.ptLine.y + line.fLineAscent + ptOffset.y);
349
350            pDevice->DrawPath(&pathSelBK, pUser2Device, NULL, crSelBK, 0,
351                              FXFILL_WINDING);
352          }
353
354          if (place.LineCmp(oldplace) != 0 ||
355              word.WordProps.fCharSpace > 0.0f ||
356              word.WordProps.nHorzScale != 100 ||
357              FXSYS_memcmp(&word.WordProps, &wp, sizeof(CPVT_WordProps)) != 0 ||
358              crOld != crCurText) {
359            if (sTextBuf.GetLength() > 0) {
360              DrawTextString(
361                  pDevice, CPDF_Point(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
362                  pFontMap->GetPDFFont(wp.nFontIndex), wp.fFontSize,
363                  pUser2Device, sTextBuf.GetByteString(), crOld, 0,
364                  wp.nHorzScale);
365
366              sTextBuf.Clear();
367            }
368            wp = word.WordProps;
369            ptBT = word.ptWord;
370            crOld = crCurText;
371          }
372
373          sTextBuf << GetPDFWordString(pFontMap, word.WordProps.nFontIndex,
374                                       word.Word, 0);
375
376          if (word.WordProps.nWordStyle & PVTWORD_STYLE_UNDERLINE) {
377            CFX_PathData pathUnderline;
378            CPDF_Rect rcUnderline = GetUnderLineRect(word);
379            pathUnderline.AppendRect(rcUnderline.left, rcUnderline.bottom,
380                                     rcUnderline.right, rcUnderline.top);
381
382            pDevice->DrawPath(&pathUnderline, pUser2Device, NULL, crCurText, 0,
383                              FXFILL_WINDING);
384          }
385
386          if (word.WordProps.nWordStyle & PVTWORD_STYLE_CROSSOUT) {
387            CFX_PathData pathCrossout;
388            CPDF_Rect rcCrossout = GetCrossoutRect(word);
389            pathCrossout.AppendRect(rcCrossout.left, rcCrossout.bottom,
390                                    rcCrossout.right, rcCrossout.top);
391
392            pDevice->DrawPath(&pathCrossout, pUser2Device, NULL, crCurText, 0,
393                              FXFILL_WINDING);
394          }
395
396          oldplace = place;
397        }
398      }
399
400      if (sTextBuf.GetLength() > 0) {
401        DrawTextString(
402            pDevice, CPDF_Point(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
403            pFontMap->GetPDFFont(wp.nFontIndex), wp.fFontSize, pUser2Device,
404            sTextBuf.GetByteString(), crOld, 0, wp.nHorzScale);
405      }
406    }
407  }
408
409  pDevice->RestoreState();
410}
411
412static void AddRectToPageObjects(CPDF_PageObjects* pPageObjs,
413                                 FX_COLORREF crFill,
414                                 const CPDF_Rect& rcFill) {
415  CPDF_PathObject* pPathObj = new CPDF_PathObject;
416  CPDF_PathData* pPathData = pPathObj->m_Path.GetModify();
417  pPathData->AppendRect(rcFill.left, rcFill.bottom, rcFill.right, rcFill.top);
418
419  FX_FLOAT rgb[3];
420  rgb[0] = FXARGB_R(crFill) / 255.0f;
421  rgb[1] = FXARGB_G(crFill) / 255.0f;
422  rgb[2] = FXARGB_B(crFill) / 255.0f;
423  pPathObj->m_ColorState.SetFillColor(
424      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3);
425
426  pPathObj->m_FillType = FXFILL_ALTERNATE;
427  pPathObj->m_bStroke = FALSE;
428
429  pPageObjs->InsertObject(pPageObjs->GetLastObjectPosition(), pPathObj);
430}
431
432static CPDF_TextObject* AddTextObjToPageObjects(CPDF_PageObjects* pPageObjs,
433                                                FX_COLORREF crText,
434                                                CPDF_Font* pFont,
435                                                FX_FLOAT fFontSize,
436                                                FX_FLOAT fCharSpace,
437                                                int32_t nHorzScale,
438                                                const CPDF_Point& point,
439                                                const CFX_ByteString& text) {
440  CPDF_TextObject* pTxtObj = new CPDF_TextObject;
441
442  CPDF_TextStateData* pTextStateData = pTxtObj->m_TextState.GetModify();
443  pTextStateData->m_pFont = pFont;
444  pTextStateData->m_FontSize = fFontSize;
445  pTextStateData->m_CharSpace = fCharSpace;
446  pTextStateData->m_WordSpace = 0;
447  pTextStateData->m_TextMode = 0;
448  pTextStateData->m_Matrix[0] = nHorzScale / 100.0f;
449  pTextStateData->m_Matrix[1] = 0;
450  pTextStateData->m_Matrix[2] = 0;
451  pTextStateData->m_Matrix[3] = 1;
452
453  FX_FLOAT rgb[3];
454  rgb[0] = FXARGB_R(crText) / 255.0f;
455  rgb[1] = FXARGB_G(crText) / 255.0f;
456  rgb[2] = FXARGB_B(crText) / 255.0f;
457  pTxtObj->m_ColorState.SetFillColor(
458      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3);
459  pTxtObj->m_ColorState.SetStrokeColor(
460      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3);
461
462  pTxtObj->SetPosition(point.x, point.y);
463  pTxtObj->SetText(text);
464
465  pPageObjs->InsertObject(pPageObjs->GetLastObjectPosition(), pTxtObj);
466
467  return pTxtObj;
468}
469
470void IFX_Edit::GeneratePageObjects(
471    CPDF_PageObjects* pPageObjects,
472    IFX_Edit* pEdit,
473    const CPDF_Point& ptOffset,
474    const CPVT_WordRange* pRange,
475    FX_COLORREF crText,
476    CFX_ArrayTemplate<CPDF_TextObject*>& ObjArray) {
477  FX_FLOAT fFontSize = pEdit->GetFontSize();
478
479  int32_t nOldFontIndex = -1;
480
481  CFX_ByteTextBuf sTextBuf;
482  CPDF_Point ptBT(0.0f, 0.0f);
483
484  ObjArray.RemoveAll();
485
486  if (IFX_Edit_Iterator* pIterator = pEdit->GetIterator()) {
487    if (IFX_Edit_FontMap* pFontMap = pEdit->GetFontMap()) {
488      if (pRange)
489        pIterator->SetAt(pRange->BeginPos);
490      else
491        pIterator->SetAt(0);
492
493      CPVT_WordPlace oldplace;
494
495      while (pIterator->NextWord()) {
496        CPVT_WordPlace place = pIterator->GetAt();
497        if (pRange && place.WordCmp(pRange->EndPos) > 0)
498          break;
499
500        CPVT_Word word;
501        if (pIterator->GetWord(word)) {
502          if (place.LineCmp(oldplace) != 0 ||
503              nOldFontIndex != word.nFontIndex) {
504            if (sTextBuf.GetLength() > 0) {
505              ObjArray.Add(AddTextObjToPageObjects(
506                  pPageObjects, crText, pFontMap->GetPDFFont(nOldFontIndex),
507                  fFontSize, 0.0f, 100,
508                  CPDF_Point(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
509                  sTextBuf.GetByteString()));
510
511              sTextBuf.Clear();
512            }
513
514            ptBT = word.ptWord;
515            nOldFontIndex = word.nFontIndex;
516          }
517
518          sTextBuf << GetPDFWordString(pFontMap, word.nFontIndex, word.Word, 0);
519          oldplace = place;
520        }
521      }
522
523      if (sTextBuf.GetLength() > 0) {
524        ObjArray.Add(AddTextObjToPageObjects(
525            pPageObjects, crText, pFontMap->GetPDFFont(nOldFontIndex),
526            fFontSize, 0.0f, 100,
527            CPDF_Point(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
528            sTextBuf.GetByteString()));
529      }
530    }
531  }
532}
533
534void IFX_Edit::GenerateRichPageObjects(
535    CPDF_PageObjects* pPageObjects,
536    IFX_Edit* pEdit,
537    const CPDF_Point& ptOffset,
538    const CPVT_WordRange* pRange,
539    CFX_ArrayTemplate<CPDF_TextObject*>& ObjArray) {
540  FX_COLORREF crCurText = ArgbEncode(255, 0, 0, 0);
541  FX_COLORREF crOld = crCurText;
542
543  CFX_ByteTextBuf sTextBuf;
544  CPVT_WordProps wp;
545  CPDF_Point ptBT(0.0f, 0.0f);
546
547  ObjArray.RemoveAll();
548
549  if (IFX_Edit_Iterator* pIterator = pEdit->GetIterator()) {
550    if (IFX_Edit_FontMap* pFontMap = pEdit->GetFontMap()) {
551      if (pRange)
552        pIterator->SetAt(pRange->BeginPos);
553      else
554        pIterator->SetAt(0);
555
556      CPVT_WordPlace oldplace;
557
558      while (pIterator->NextWord()) {
559        CPVT_WordPlace place = pIterator->GetAt();
560        if (pRange && place.WordCmp(pRange->EndPos) > 0)
561          break;
562
563        CPVT_Word word;
564        if (pIterator->GetWord(word)) {
565          word.WordProps.fFontSize = word.fFontSize;
566
567          crCurText = ArgbEncode(255, word.WordProps.dwWordColor);
568
569          if (place.LineCmp(oldplace) != 0 ||
570              word.WordProps.fCharSpace > 0.0f ||
571              word.WordProps.nHorzScale != 100 ||
572              FXSYS_memcmp(&word.WordProps, &wp, sizeof(CPVT_WordProps)) != 0 ||
573              crOld != crCurText) {
574            if (sTextBuf.GetLength() > 0) {
575              ObjArray.Add(AddTextObjToPageObjects(
576                  pPageObjects, crOld, pFontMap->GetPDFFont(wp.nFontIndex),
577                  wp.fFontSize, wp.fCharSpace, wp.nHorzScale,
578                  CPDF_Point(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
579                  sTextBuf.GetByteString()));
580
581              sTextBuf.Clear();
582            }
583
584            wp = word.WordProps;
585            ptBT = word.ptWord;
586            crOld = crCurText;
587          }
588
589          sTextBuf << GetPDFWordString(pFontMap, word.WordProps.nFontIndex,
590                                       word.Word, 0);
591
592          if (word.WordProps.nWordStyle & PVTWORD_STYLE_UNDERLINE) {
593            CPDF_Rect rcUnderline = GetUnderLineRect(word);
594            rcUnderline.left += ptOffset.x;
595            rcUnderline.right += ptOffset.x;
596            rcUnderline.top += ptOffset.y;
597            rcUnderline.bottom += ptOffset.y;
598
599            AddRectToPageObjects(pPageObjects, crCurText, rcUnderline);
600          }
601
602          if (word.WordProps.nWordStyle & PVTWORD_STYLE_CROSSOUT) {
603            CPDF_Rect rcCrossout = GetCrossoutRect(word);
604            rcCrossout.left += ptOffset.x;
605            rcCrossout.right += ptOffset.x;
606            rcCrossout.top += ptOffset.y;
607            rcCrossout.bottom += ptOffset.y;
608
609            AddRectToPageObjects(pPageObjects, crCurText, rcCrossout);
610          }
611
612          oldplace = place;
613        }
614      }
615
616      if (sTextBuf.GetLength() > 0) {
617        ObjArray.Add(AddTextObjToPageObjects(
618            pPageObjects, crOld, pFontMap->GetPDFFont(wp.nFontIndex),
619            wp.fFontSize, wp.fCharSpace, wp.nHorzScale,
620            CPDF_Point(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y),
621            sTextBuf.GetByteString()));
622      }
623    }
624  }
625}
626
627void IFX_Edit::GenerateUnderlineObjects(CPDF_PageObjects* pPageObjects,
628                                        IFX_Edit* pEdit,
629                                        const CPDF_Point& ptOffset,
630                                        const CPVT_WordRange* pRange,
631                                        FX_COLORREF color) {
632  if (IFX_Edit_Iterator* pIterator = pEdit->GetIterator()) {
633    if (pEdit->GetFontMap()) {
634      if (pRange)
635        pIterator->SetAt(pRange->BeginPos);
636      else
637        pIterator->SetAt(0);
638
639      CPVT_WordPlace oldplace;
640
641      while (pIterator->NextWord()) {
642        CPVT_WordPlace place = pIterator->GetAt();
643        if (pRange && place.WordCmp(pRange->EndPos) > 0)
644          break;
645
646        CPVT_Word word;
647        if (pIterator->GetWord(word)) {
648          CPDF_Rect rcUnderline = GetUnderLineRect(word);
649          rcUnderline.left += ptOffset.x;
650          rcUnderline.right += ptOffset.x;
651          rcUnderline.top += ptOffset.y;
652          rcUnderline.bottom += ptOffset.y;
653          AddRectToPageObjects(pPageObjects, color, rcUnderline);
654        }
655      }
656    }
657  }
658}
659