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