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