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_resource.h"
8#include "fpdfsdk/include/fxedit/fx_edit.h"
9#include "fpdfsdk/include/fxedit/fxet_edit.h"
10
11CFX_ByteString GetPDFWordString(IFX_Edit_FontMap* pFontMap,
12                                int32_t nFontIndex,
13                                FX_WORD Word,
14                                FX_WORD SubWord) {
15  CFX_ByteString sWord;
16  if (CPDF_Font* pPDFFont = pFontMap->GetPDFFont(nFontIndex)) {
17    if (SubWord > 0) {
18      Word = SubWord;
19    } else {
20      FX_DWORD dwCharCode = -1;
21
22      if (pPDFFont->IsUnicodeCompatible())
23        dwCharCode = pPDFFont->CharCodeFromUnicode(Word);
24      else
25        dwCharCode = pFontMap->CharCodeFromUnicode(nFontIndex, Word);
26
27      if (dwCharCode > 0) {
28        pPDFFont->AppendChar(sWord, dwCharCode);
29        return sWord;
30      }
31    }
32
33    pPDFFont->AppendChar(sWord, Word);
34  }
35
36  return sWord;
37}
38
39static CFX_ByteString GetWordRenderString(const CFX_ByteString& strWords) {
40  if (strWords.GetLength() > 0)
41    return PDF_EncodeString(strWords) + " Tj\n";
42
43  return "";
44}
45
46static CFX_ByteString GetFontSetString(IFX_Edit_FontMap* pFontMap,
47                                       int32_t nFontIndex,
48                                       FX_FLOAT fFontSize) {
49  CFX_ByteTextBuf sRet;
50
51  if (pFontMap) {
52    CFX_ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
53
54    if (sFontAlias.GetLength() > 0 && fFontSize > 0)
55      sRet << "/" << sFontAlias << " " << fFontSize << " Tf\n";
56  }
57
58  return sRet.GetByteString();
59}
60
61CFX_ByteString IFX_Edit::GetEditAppearanceStream(
62    IFX_Edit* pEdit,
63    const CPDF_Point& ptOffset,
64    const CPVT_WordRange* pRange /* = NULL*/,
65    FX_BOOL bContinuous /* = TRUE*/,
66    FX_WORD SubWord /* = 0*/) {
67  CFX_ByteTextBuf sEditStream, sWords;
68
69  CPDF_Point ptOld(0.0f, 0.0f), ptNew(0.0f, 0.0f);
70  int32_t nCurFontIndex = -1;
71
72  if (IFX_Edit_Iterator* pIterator = pEdit->GetIterator()) {
73    if (pRange)
74      pIterator->SetAt(pRange->BeginPos);
75    else
76      pIterator->SetAt(0);
77
78    CPVT_WordPlace oldplace;
79
80    while (pIterator->NextWord()) {
81      CPVT_WordPlace place = pIterator->GetAt();
82
83      if (pRange && place.WordCmp(pRange->EndPos) > 0)
84        break;
85
86      if (bContinuous) {
87        if (place.LineCmp(oldplace) != 0) {
88          if (sWords.GetSize() > 0) {
89            sEditStream << GetWordRenderString(sWords.GetByteString());
90            sWords.Clear();
91          }
92
93          CPVT_Word word;
94          if (pIterator->GetWord(word)) {
95            ptNew = CPDF_Point(word.ptWord.x + ptOffset.x,
96                               word.ptWord.y + ptOffset.y);
97          } else {
98            CPVT_Line line;
99            pIterator->GetLine(line);
100            ptNew = CPDF_Point(line.ptLine.x + ptOffset.x,
101                               line.ptLine.y + ptOffset.y);
102          }
103
104          if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
105            sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
106                        << " Td\n";
107
108            ptOld = ptNew;
109          }
110        }
111
112        CPVT_Word word;
113        if (pIterator->GetWord(word)) {
114          if (word.nFontIndex != nCurFontIndex) {
115            if (sWords.GetSize() > 0) {
116              sEditStream << GetWordRenderString(sWords.GetByteString());
117              sWords.Clear();
118            }
119            sEditStream << GetFontSetString(pEdit->GetFontMap(),
120                                            word.nFontIndex, word.fFontSize);
121            nCurFontIndex = word.nFontIndex;
122          }
123
124          sWords << GetPDFWordString(pEdit->GetFontMap(), nCurFontIndex,
125                                     word.Word, SubWord);
126        }
127
128        oldplace = place;
129      } else {
130        CPVT_Word word;
131        if (pIterator->GetWord(word)) {
132          ptNew = CPDF_Point(word.ptWord.x + ptOffset.x,
133                             word.ptWord.y + ptOffset.y);
134
135          if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
136            sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
137                        << " Td\n";
138            ptOld = ptNew;
139          }
140
141          if (word.nFontIndex != nCurFontIndex) {
142            sEditStream << GetFontSetString(pEdit->GetFontMap(),
143                                            word.nFontIndex, word.fFontSize);
144            nCurFontIndex = word.nFontIndex;
145          }
146
147          sEditStream << GetWordRenderString(GetPDFWordString(
148              pEdit->GetFontMap(), nCurFontIndex, word.Word, SubWord));
149        }
150      }
151    }
152
153    if (sWords.GetSize() > 0) {
154      sEditStream << GetWordRenderString(sWords.GetByteString());
155      sWords.Clear();
156    }
157  }
158
159  CFX_ByteTextBuf sAppStream;
160  if (sEditStream.GetSize() > 0) {
161    int32_t nHorzScale = pEdit->GetHorzScale();
162    if (nHorzScale != 100) {
163      sAppStream << nHorzScale << " Tz\n";
164    }
165
166    FX_FLOAT fCharSpace = pEdit->GetCharSpace();
167    if (!FX_EDIT_IsFloatZero(fCharSpace)) {
168      sAppStream << fCharSpace << " Tc\n";
169    }
170
171    sAppStream << sEditStream;
172  }
173
174  return sAppStream.GetByteString();
175}
176
177CFX_ByteString IFX_Edit::GetSelectAppearanceStream(
178    IFX_Edit* pEdit,
179    const CPDF_Point& ptOffset,
180    const CPVT_WordRange* pRange) {
181  CFX_ByteTextBuf sRet;
182
183  if (pRange && pRange->IsExist()) {
184    if (IFX_Edit_Iterator* pIterator = pEdit->GetIterator()) {
185      pIterator->SetAt(pRange->BeginPos);
186
187      while (pIterator->NextWord()) {
188        CPVT_WordPlace place = pIterator->GetAt();
189
190        if (pRange && place.WordCmp(pRange->EndPos) > 0)
191          break;
192
193        CPVT_Word word;
194        CPVT_Line line;
195        if (pIterator->GetWord(word) && pIterator->GetLine(line)) {
196          sRet << word.ptWord.x + ptOffset.x << " "
197               << line.ptLine.y + line.fLineDescent << " " << word.fWidth << " "
198               << line.fLineAscent - line.fLineDescent << " re\nf\n";
199        }
200      }
201    }
202  }
203
204  return sRet.GetByteString();
205}
206