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/fxge/fx_ge.h"
8#include "../../../include/fpdfapi/fpdf_render.h"
9#include "../../../include/fpdfapi/fpdf_pageobj.h"
10#include "../fpdf_page/pageint.h"
11#include "render_int.h"
12extern FX_BOOL IsAvailableMatrix(const CFX_AffineMatrix& matrix);
13CPDF_Type3Cache::~CPDF_Type3Cache()
14{
15    FX_POSITION pos = m_SizeMap.GetStartPosition();
16    CFX_ByteString Key;
17    CPDF_Type3Glyphs* pSizeCache = NULL;
18    while(pos) {
19        pSizeCache = (CPDF_Type3Glyphs*)m_SizeMap.GetNextValue(pos);
20        delete pSizeCache;
21    }
22    m_SizeMap.RemoveAll();
23}
24CFX_GlyphBitmap* CPDF_Type3Cache::LoadGlyph(FX_DWORD charcode, const CFX_AffineMatrix* pMatrix, FX_FLOAT retinaScaleX, FX_FLOAT retinaScaleY)
25{
26    _CPDF_UniqueKeyGen keygen;
27    keygen.Generate(4, FXSYS_round(pMatrix->a * 10000), FXSYS_round(pMatrix->b * 10000),
28                    FXSYS_round(pMatrix->c * 10000), FXSYS_round(pMatrix->d * 10000));
29    CFX_ByteStringC FaceGlyphsKey(keygen.m_Key, keygen.m_KeyLen);
30    CPDF_Type3Glyphs* pSizeCache = NULL;
31    if(!m_SizeMap.Lookup(FaceGlyphsKey, (void*&)pSizeCache)) {
32        pSizeCache = FX_NEW CPDF_Type3Glyphs;
33        m_SizeMap.SetAt(FaceGlyphsKey, pSizeCache);
34    }
35    CFX_GlyphBitmap* pGlyphBitmap;
36    if(pSizeCache->m_GlyphMap.Lookup((FX_LPVOID)(FX_UINTPTR)charcode, (void*&)pGlyphBitmap)) {
37        return pGlyphBitmap;
38    }
39    pGlyphBitmap = RenderGlyph(pSizeCache, charcode, pMatrix, retinaScaleX, retinaScaleY);
40    pSizeCache->m_GlyphMap.SetAt((FX_LPVOID)(FX_UINTPTR)charcode, pGlyphBitmap);
41    return pGlyphBitmap;
42}
43CPDF_Type3Glyphs::~CPDF_Type3Glyphs()
44{
45    FX_POSITION pos = m_GlyphMap.GetStartPosition();
46    FX_LPVOID Key;
47    CFX_GlyphBitmap* pGlyphBitmap;
48    while(pos) {
49        m_GlyphMap.GetNextAssoc(pos, Key, (void*&)pGlyphBitmap);
50        delete pGlyphBitmap;
51    }
52}
53static int _AdjustBlue(FX_FLOAT pos, int& count, int blues[])
54{
55    FX_FLOAT min_distance = 1000000.0f * 1.0f;
56    int closest_pos = -1;
57    for (int i = 0; i < count; i ++) {
58        FX_FLOAT distance = (FX_FLOAT)FXSYS_fabs(pos - (FX_FLOAT)blues[i]);
59        if (distance < 1.0f * 80.0f / 100.0f && distance < min_distance) {
60            min_distance = distance;
61            closest_pos = i;
62        }
63    }
64    if (closest_pos >= 0) {
65        return blues[closest_pos];
66    }
67    int new_pos = FXSYS_round(pos);
68    if (count == TYPE3_MAX_BLUES) {
69        return new_pos;
70    }
71    blues[count++] = new_pos;
72    return new_pos;
73}
74void CPDF_Type3Glyphs::AdjustBlue(FX_FLOAT top, FX_FLOAT bottom, int& top_line, int& bottom_line)
75{
76    top_line = _AdjustBlue(top, m_TopBlueCount, m_TopBlue);
77    bottom_line = _AdjustBlue(bottom, m_BottomBlueCount, m_BottomBlue);
78}
79static FX_BOOL _IsScanLine1bpp(FX_LPBYTE pBuf, int width)
80{
81    int size = width / 8;
82    for (int i = 0; i < size; i ++)
83        if (pBuf[i]) {
84            return TRUE;
85        }
86    if (width % 8)
87        if (pBuf[width / 8] & (0xff << (8 - width % 8))) {
88            return TRUE;
89        }
90    return FALSE;
91}
92static FX_BOOL _IsScanLine8bpp(FX_LPBYTE pBuf, int width)
93{
94    for (int i = 0; i < width; i ++)
95        if (pBuf[i] > 0x40) {
96            return TRUE;
97        }
98    return FALSE;
99}
100static int _DetectFirstLastScan(const CFX_DIBitmap* pBitmap, FX_BOOL bFirst)
101{
102    int height = pBitmap->GetHeight(), pitch = pBitmap->GetPitch(), width = pBitmap->GetWidth();
103    int bpp = pBitmap->GetBPP();
104    if (bpp > 8) {
105        width *= bpp / 8;
106    }
107    FX_LPBYTE pBuf = pBitmap->GetBuffer();
108    int line = bFirst ? 0 : height - 1;
109    int line_step = bFirst ? 1 : -1;
110    int line_end = bFirst ? height : -1;
111    while (line != line_end) {
112        if (bpp == 1) {
113            if (_IsScanLine1bpp(pBuf + line * pitch, width)) {
114                return line;
115            }
116        } else {
117            if (_IsScanLine8bpp(pBuf + line * pitch, width)) {
118                return line;
119            }
120        }
121        line += line_step;
122    }
123    return -1;
124}
125CFX_GlyphBitmap* CPDF_Type3Cache::RenderGlyph(CPDF_Type3Glyphs* pSize, FX_DWORD charcode, const CFX_AffineMatrix* pMatrix, FX_FLOAT retinaScaleX, FX_FLOAT retinaScaleY)
126{
127    CPDF_Type3Char* pChar = m_pFont->LoadChar(charcode);
128    if (pChar == NULL || pChar->m_pBitmap == NULL) {
129        return NULL;
130    }
131    CFX_DIBitmap* pBitmap = pChar->m_pBitmap;
132    CFX_AffineMatrix image_matrix, text_matrix;
133    image_matrix = pChar->m_ImageMatrix;
134    text_matrix.Set(pMatrix->a, pMatrix->b, pMatrix->c, pMatrix->d, 0, 0);
135    image_matrix.Concat(text_matrix);
136    CFX_DIBitmap* pResBitmap = NULL;
137    int left, top;
138    if (FXSYS_fabs(image_matrix.b) < FXSYS_fabs(image_matrix.a) / 100 && FXSYS_fabs(image_matrix.c) < FXSYS_fabs(image_matrix.d) / 100) {
139        int top_line, bottom_line;
140        top_line = _DetectFirstLastScan(pBitmap, TRUE);
141        bottom_line = _DetectFirstLastScan(pBitmap, FALSE);
142        if (top_line == 0 && bottom_line == pBitmap->GetHeight() - 1) {
143            FX_FLOAT top_y = image_matrix.d + image_matrix.f;
144            FX_FLOAT bottom_y = image_matrix.f;
145            FX_BOOL bFlipped = top_y > bottom_y;
146            if (bFlipped) {
147                FX_FLOAT temp = top_y;
148                top_y = bottom_y;
149                bottom_y = temp;
150            }
151            pSize->AdjustBlue(top_y, bottom_y, top_line, bottom_line);
152            pResBitmap = pBitmap->StretchTo((int)(FXSYS_round(image_matrix.a) * retinaScaleX), (int)((bFlipped ? top_line - bottom_line : bottom_line - top_line) * retinaScaleY));
153            top = top_line;
154            if (image_matrix.a < 0) {
155                image_matrix.Scale(retinaScaleX, retinaScaleY);
156                left = FXSYS_round(image_matrix.e + image_matrix.a);
157            } else {
158                left = FXSYS_round(image_matrix.e);
159            }
160        } else {
161        }
162    }
163    if (pResBitmap == NULL) {
164        image_matrix.Scale(retinaScaleX, retinaScaleY);
165        pResBitmap = pBitmap->TransformTo(&image_matrix, left, top);
166    }
167    if (pResBitmap == NULL) {
168        return NULL;
169    }
170    CFX_GlyphBitmap* pGlyph = FX_NEW CFX_GlyphBitmap;
171    pGlyph->m_Left = left;
172    pGlyph->m_Top = -top;
173    pGlyph->m_Bitmap.TakeOver(pResBitmap);
174    delete pResBitmap;
175    return pGlyph;
176}
177void _CPDF_UniqueKeyGen::Generate(int count, ...)
178{
179    va_list argList;
180    va_start(argList, count);
181    for (int i = 0; i < count; i ++) {
182        int p = va_arg(argList, int);
183        ((FX_DWORD*)m_Key)[i] = p;
184    }
185    va_end(argList);
186    m_KeyLen = count * sizeof(FX_DWORD);
187}
188FX_BOOL CPDF_RenderStatus::ProcessText(const CPDF_TextObject* textobj, const CFX_AffineMatrix* pObj2Device, CFX_PathData* pClippingPath)
189{
190    if(textobj->m_nChars == 0) {
191        return TRUE;
192    }
193    int text_render_mode = textobj->m_TextState.GetObject()->m_TextMode;
194    if (text_render_mode == 3) {
195        return TRUE;
196    }
197    CPDF_Font* pFont = textobj->m_TextState.GetFont();
198    if (pFont->GetFontType() == PDFFONT_TYPE3) {
199        return ProcessType3Text(textobj, pObj2Device);
200    }
201    FX_BOOL bFill = FALSE, bStroke = FALSE, bClip = FALSE;
202    if (pClippingPath) {
203        bClip = TRUE;
204    } else {
205        switch (text_render_mode) {
206            case 0:
207            case 4:
208                bFill = TRUE;
209                break;
210            case 1:
211            case 5:
212                if (pFont->GetFace() == NULL && !(pFont->GetSubstFont()->m_SubstFlags & FXFONT_SUBST_GLYPHPATH)) {
213                    bFill = TRUE;
214                } else {
215                    bStroke = TRUE;
216                }
217                break;
218            case 2:
219            case 6:
220                if (pFont->GetFace() == NULL && !(pFont->GetSubstFont()->m_SubstFlags & FXFONT_SUBST_GLYPHPATH)) {
221                    bFill = TRUE;
222                } else {
223                    bFill = bStroke = TRUE;
224                }
225                break;
226            case 3:
227            case 7:
228                return TRUE;
229            default:
230                bFill = TRUE;
231        }
232    }
233    FX_ARGB stroke_argb = 0, fill_argb = 0;
234    FX_BOOL bPattern = FALSE;
235    if (bStroke) {
236        if (textobj->m_ColorState.GetStrokeColor()->IsPattern()) {
237            bPattern = TRUE;
238        } else {
239            stroke_argb = GetStrokeArgb(textobj);
240        }
241    }
242    if (bFill) {
243        if (textobj->m_ColorState.GetFillColor()->IsPattern()) {
244            bPattern = TRUE;
245        } else {
246            fill_argb = GetFillArgb(textobj);
247        }
248    }
249    CFX_AffineMatrix text_matrix;
250    textobj->GetTextMatrix(&text_matrix);
251    if(IsAvailableMatrix(text_matrix) == FALSE) {
252        return TRUE;
253    }
254    FX_FLOAT font_size = textobj->m_TextState.GetFontSize();
255    if (bPattern) {
256        DrawTextPathWithPattern(textobj, pObj2Device, pFont, font_size, &text_matrix, bFill, bStroke);
257        return TRUE;
258    }
259#if defined(_FPDFAPI_MINI_)
260    if (bFill) {
261        bStroke = FALSE;
262    }
263    if (bStroke) {
264        if (font_size * text_matrix.GetXUnit() * pObj2Device->GetXUnit() < 6) {
265            bStroke = FALSE;
266        }
267    }
268#endif
269    if (bClip || bStroke) {
270        const CFX_AffineMatrix* pDeviceMatrix = pObj2Device;
271        CFX_AffineMatrix device_matrix;
272        if (bStroke) {
273            const FX_FLOAT* pCTM = textobj->m_TextState.GetObject()->m_CTM;
274            if (pCTM[0] != 1.0f || pCTM[3] != 1.0f) {
275                CFX_AffineMatrix ctm(pCTM[0], pCTM[1], pCTM[2], pCTM[3], 0, 0);
276                text_matrix.ConcatInverse(ctm);
277                device_matrix.Copy(ctm);
278                device_matrix.Concat(*pObj2Device);
279                pDeviceMatrix = &device_matrix;
280            }
281        }
282        int flag = 0;
283        if (bStroke && bFill) {
284            flag |= FX_FILL_STROKE;
285            flag |= FX_STROKE_TEXT_MODE;
286        }
287#if !defined(_FPDFAPI_MINI_) || defined(_FXCORE_FEATURE_ALL_)
288        const CPDF_GeneralStateData* pGeneralData = ((CPDF_PageObject*)textobj)->m_GeneralState;
289        if (pGeneralData && pGeneralData->m_StrokeAdjust) {
290            flag |= FX_STROKE_ADJUST;
291        }
292#endif
293        if (m_Options.m_Flags & RENDER_NOTEXTSMOOTH) {
294            flag |= FXFILL_NOPATHSMOOTH;
295        }
296        return CPDF_TextRenderer::DrawTextPath(m_pDevice, textobj->m_nChars, textobj->m_pCharCodes, textobj->m_pCharPos, pFont, font_size,
297                                               &text_matrix, pDeviceMatrix, textobj->m_GraphState, fill_argb, stroke_argb, pClippingPath, flag);
298    }
299    text_matrix.Concat(*pObj2Device);
300    return CPDF_TextRenderer::DrawNormalText(m_pDevice, textobj->m_nChars, textobj->m_pCharCodes, textobj->m_pCharPos, pFont, font_size,
301            &text_matrix, fill_argb, &m_Options);
302}
303CPDF_Type3Cache* CPDF_RenderStatus::GetCachedType3(CPDF_Type3Font* pFont)
304{
305    if (pFont->m_pDocument == NULL) {
306        return NULL;
307    }
308    pFont->m_pDocument->GetPageData()->GetFont(pFont->GetFontDict(), FALSE);
309    return pFont->m_pDocument->GetRenderData()->GetCachedType3(pFont);
310}
311static void ReleaseCachedType3(CPDF_Type3Font* pFont)
312{
313    if (pFont->m_pDocument == NULL) {
314        return;
315    }
316    pFont->m_pDocument->GetRenderData()->ReleaseCachedType3(pFont);
317    pFont->m_pDocument->GetPageData()->ReleaseFont(pFont->GetFontDict());
318}
319FX_BOOL CPDF_Type3Char::LoadBitmap(CPDF_RenderContext* pContext)
320{
321    if (m_pBitmap != NULL || m_pForm == NULL) {
322        return TRUE;
323    }
324    if (m_pForm->CountObjects() == 1 && !m_bColored) {
325        CPDF_PageObject *pPageObj = m_pForm->GetObjectAt(m_pForm->GetFirstObjectPosition());
326        if (pPageObj->m_Type == PDFPAGE_IMAGE) {
327            CPDF_ImageObject* pImage = (CPDF_ImageObject*)pPageObj;
328            m_ImageMatrix = pImage->m_Matrix;
329            const CFX_DIBSource* pSource = pImage->m_pImage->LoadDIBSource();
330            if (pSource) {
331                m_pBitmap = pSource->Clone();
332                delete pSource;
333            }
334            delete m_pForm;
335            m_pForm = NULL;
336            return TRUE;
337        }
338        if (pPageObj->m_Type == PDFPAGE_INLINES) {
339            CPDF_InlineImages *pInlines = (CPDF_InlineImages *)pPageObj;
340            if (pInlines->m_pStream) {
341                m_ImageMatrix = pInlines->m_Matrices[0];
342                CPDF_DIBSource dibsrc;
343                if (!dibsrc.Load(pContext->m_pDocument, pInlines->m_pStream, NULL, NULL, NULL, NULL)) {
344                    return FALSE;
345                }
346                m_pBitmap = dibsrc.Clone();
347                delete m_pForm;
348                m_pForm = NULL;
349                return TRUE;
350            }
351        }
352    }
353    return FALSE;
354}
355class CPDF_RefType3Cache
356{
357public:
358    CPDF_RefType3Cache(CPDF_Type3Font* pType3Font)
359    {
360        m_dwCount = 0;
361        m_pType3Font = pType3Font;
362    }
363    ~CPDF_RefType3Cache()
364    {
365        while(m_dwCount--) {
366            ReleaseCachedType3(m_pType3Font);
367        }
368    }
369    FX_DWORD m_dwCount;
370    CPDF_Type3Font* m_pType3Font;
371};
372FX_BOOL CPDF_RenderStatus::ProcessType3Text(const CPDF_TextObject* textobj, const CFX_AffineMatrix* pObj2Device)
373{
374    CPDF_Type3Font* pType3Font = textobj->m_TextState.GetFont()->GetType3Font();
375    for (int j = 0; j < m_Type3FontCache.GetSize(); j++)
376        if ((CPDF_Type3Font*)m_Type3FontCache.GetAt(j) == pType3Font) {
377            return TRUE;
378        }
379    CFX_Matrix dCTM = m_pDevice->GetCTM();
380    FX_FLOAT sa = FXSYS_fabs(dCTM.a);
381    FX_FLOAT sd = FXSYS_fabs(dCTM.d);
382    CFX_AffineMatrix text_matrix;
383    textobj->GetTextMatrix(&text_matrix);
384    CFX_AffineMatrix char_matrix = pType3Font->GetFontMatrix();
385    FX_FLOAT font_size = textobj->m_TextState.GetFontSize();
386    char_matrix.Scale(font_size, font_size);
387    FX_ARGB fill_argb = GetFillArgb(textobj, TRUE);
388    int fill_alpha = FXARGB_A(fill_argb);
389    int device_class = m_pDevice->GetDeviceClass();
390    FXTEXT_GLYPHPOS* pGlyphAndPos = NULL;
391    if (device_class == FXDC_DISPLAY) {
392        pGlyphAndPos = FX_Alloc(FXTEXT_GLYPHPOS, textobj->m_nChars);
393        FXSYS_memset32(pGlyphAndPos, 0, sizeof(FXTEXT_GLYPHPOS) * textobj->m_nChars);
394    } else if (fill_alpha < 255) {
395        return FALSE;
396    }
397    CPDF_RefType3Cache refTypeCache(pType3Font);
398    FX_DWORD *pChars = textobj->m_pCharCodes;
399    if (textobj->m_nChars == 1) {
400        pChars = (FX_DWORD*)(&textobj->m_pCharCodes);
401    }
402    for (int iChar = 0; iChar < textobj->m_nChars; iChar ++) {
403        FX_DWORD charcode = pChars[iChar];
404        if (charcode == (FX_DWORD) - 1) {
405            continue;
406        }
407        CPDF_Type3Char* pType3Char = pType3Font->LoadChar(charcode);
408        if (pType3Char == NULL) {
409            continue;
410        }
411        CFX_AffineMatrix matrix = char_matrix;
412        matrix.e += iChar ? textobj->m_pCharPos[iChar - 1] : 0;
413        matrix.Concat(text_matrix);
414        matrix.Concat(*pObj2Device);
415        if (!pType3Char->LoadBitmap(m_pContext)) {
416            if (pGlyphAndPos) {
417                for (int i = 0; i < iChar; i ++) {
418                    FXTEXT_GLYPHPOS& glyph = pGlyphAndPos[i];
419                    if (glyph.m_pGlyph == NULL) {
420                        continue;
421                    }
422                    m_pDevice->SetBitMask(&glyph.m_pGlyph->m_Bitmap,
423                                          glyph.m_OriginX + glyph.m_pGlyph->m_Left,
424                                          glyph.m_OriginY - glyph.m_pGlyph->m_Top, fill_argb);
425                }
426                FX_Free(pGlyphAndPos);
427                pGlyphAndPos = NULL;
428            }
429            CPDF_GraphicStates* pStates = CloneObjStates(textobj, FALSE);
430            CPDF_RenderOptions Options = m_Options;
431            Options.m_Flags |= RENDER_FORCE_HALFTONE | RENDER_RECT_AA;
432            Options.m_Flags &= ~RENDER_FORCE_DOWNSAMPLE;
433            CPDF_Dictionary* pFormResource = NULL;
434            if (pType3Char->m_pForm && pType3Char->m_pForm->m_pFormDict) {
435                pFormResource = pType3Char->m_pForm->m_pFormDict->GetDict(FX_BSTRC("Resources"));
436            }
437            if (fill_alpha == 255) {
438                CPDF_RenderStatus status;
439                status.Initialize(m_Level + 1, m_pContext, m_pDevice, NULL, NULL, this, pStates, &Options,
440                                  pType3Char->m_pForm->m_Transparency, m_bDropObjects, pFormResource, FALSE, pType3Char, fill_argb);
441                status.m_Type3FontCache.Append(m_Type3FontCache);
442                status.m_Type3FontCache.Add(pType3Font);
443                m_pDevice->SaveState();
444                status.RenderObjectList(pType3Char->m_pForm, &matrix);
445                m_pDevice->RestoreState();
446            } else {
447                CFX_FloatRect rect_f = pType3Char->m_pForm->CalcBoundingBox();
448                rect_f.Transform(&matrix);
449                FX_RECT rect = rect_f.GetOutterRect();
450                CFX_FxgeDevice bitmap_device;
451                if (!bitmap_device.Create((int)(rect.Width() * sa), (int)(rect.Height() * sd), FXDIB_Argb)) {
452                    return TRUE;
453                }
454                bitmap_device.GetBitmap()->Clear(0);
455                CPDF_RenderStatus status;
456                status.Initialize(m_Level + 1, m_pContext, &bitmap_device, NULL, NULL, this, pStates, &Options,
457                                  pType3Char->m_pForm->m_Transparency, m_bDropObjects, pFormResource, FALSE, pType3Char, fill_argb);
458                status.m_Type3FontCache.Append(m_Type3FontCache);
459                status.m_Type3FontCache.Add(pType3Font);
460                matrix.TranslateI(-rect.left, -rect.top);
461                matrix.Scale(sa, sd);
462                status.RenderObjectList(pType3Char->m_pForm, &matrix);
463                m_pDevice->SetDIBits(bitmap_device.GetBitmap(), rect.left, rect.top);
464            }
465            delete pStates;
466        } else if (pType3Char->m_pBitmap) {
467            if (device_class == FXDC_DISPLAY) {
468                CPDF_Type3Cache* pCache = GetCachedType3(pType3Font);
469                refTypeCache.m_dwCount++;
470                CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, &matrix, sa, sd);
471                if (pBitmap == NULL) {
472                    continue;
473                }
474                int origin_x = FXSYS_round(matrix.e);
475                int origin_y = FXSYS_round(matrix.f);
476                if (pGlyphAndPos) {
477                    pGlyphAndPos[iChar].m_pGlyph = pBitmap;
478                    pGlyphAndPos[iChar].m_OriginX = origin_x;
479                    pGlyphAndPos[iChar].m_OriginY = origin_y;
480                } else {
481                    m_pDevice->SetBitMask(&pBitmap->m_Bitmap, origin_x + pBitmap->m_Left, origin_y - pBitmap->m_Top, fill_argb);
482                }
483            } else {
484                CFX_AffineMatrix image_matrix = pType3Char->m_ImageMatrix;
485                image_matrix.Concat(matrix);
486                CPDF_ImageRenderer renderer;
487                if (renderer.Start(this, pType3Char->m_pBitmap, fill_argb, 255, &image_matrix, 0, FALSE)) {
488                    renderer.Continue(NULL);
489                }
490                if (!renderer.m_Result) {
491                    return FALSE;
492                }
493            }
494        }
495    }
496    if (pGlyphAndPos) {
497        FX_RECT rect = FXGE_GetGlyphsBBox(pGlyphAndPos, textobj->m_nChars, 0, sa, sd);
498        CFX_DIBitmap bitmap;
499        if (!bitmap.Create((int)(rect.Width() * sa), (int)(rect.Height() * sd), FXDIB_8bppMask)) {
500            FX_Free(pGlyphAndPos);
501            return TRUE;
502        }
503        bitmap.Clear(0);
504        for (int iChar = 0; iChar < textobj->m_nChars; iChar ++) {
505            FXTEXT_GLYPHPOS& glyph = pGlyphAndPos[iChar];
506            if (glyph.m_pGlyph == NULL) {
507                continue;
508            }
509            bitmap.TransferBitmap((int)((glyph.m_OriginX + glyph.m_pGlyph->m_Left - rect.left) * sa),
510                                  (int)((glyph.m_OriginY - glyph.m_pGlyph->m_Top - rect.top) * sd),
511                                  glyph.m_pGlyph->m_Bitmap.GetWidth(), glyph.m_pGlyph->m_Bitmap.GetHeight(),
512                                  &glyph.m_pGlyph->m_Bitmap, 0, 0);
513        }
514        m_pDevice->SetBitMask(&bitmap, rect.left, rect.top, fill_argb);
515        FX_Free(pGlyphAndPos);
516    }
517    return TRUE;
518}
519class CPDF_CharPosList
520{
521public:
522    CPDF_CharPosList();
523    ~CPDF_CharPosList();
524    void				Load(int nChars, FX_DWORD* pCharCodes, FX_FLOAT* pCharPos, CPDF_Font* pFont, FX_FLOAT font_size);
525    FXTEXT_CHARPOS*		m_pCharPos;
526    FX_DWORD			m_nChars;
527};
528FX_FLOAT _CIDTransformToFloat(FX_BYTE ch);
529CPDF_CharPosList::CPDF_CharPosList()
530{
531    m_pCharPos = NULL;
532}
533CPDF_CharPosList::~CPDF_CharPosList()
534{
535    if (m_pCharPos) {
536        FX_Free(m_pCharPos);
537    }
538}
539void CPDF_CharPosList::Load(int nChars, FX_DWORD* pCharCodes, FX_FLOAT* pCharPos, CPDF_Font* pFont,
540                            FX_FLOAT FontSize)
541{
542    m_pCharPos = FX_Alloc(FXTEXT_CHARPOS, nChars);
543    FXSYS_memset32(m_pCharPos, 0, sizeof(FXTEXT_CHARPOS) * nChars);
544    m_nChars = 0;
545    CPDF_CIDFont* pCIDFont = pFont->GetCIDFont();
546    FX_BOOL bVertWriting = pCIDFont && pCIDFont->IsVertWriting();
547    for (int iChar = 0; iChar < nChars; iChar ++) {
548        FX_DWORD CharCode = nChars == 1 ? (FX_DWORD)(FX_UINTPTR)pCharCodes : pCharCodes[iChar];
549        if (CharCode == (FX_DWORD) - 1) {
550            continue;
551        }
552        FX_BOOL bVert = FALSE;
553        FXTEXT_CHARPOS& charpos = m_pCharPos[m_nChars++];
554        if (pCIDFont) {
555            charpos.m_bFontStyle = pCIDFont->IsFontStyleFromCharCode(CharCode);
556        }
557        charpos.m_GlyphIndex = pFont->GlyphFromCharCode(CharCode, &bVert);
558#if _FXM_PLATFORM_  == _FXM_PLATFORM_APPLE_
559        charpos.m_ExtGID = pFont->GlyphFromCharCodeExt(CharCode);
560#endif
561        if (!pFont->IsEmbedded() && pFont->GetFontType() != PDFFONT_CIDFONT) {
562            charpos.m_FontCharWidth = pFont->GetCharWidthF(CharCode);
563        } else {
564            charpos.m_FontCharWidth = 0;
565        }
566        charpos.m_OriginX = iChar ? pCharPos[iChar - 1] : 0;
567        charpos.m_OriginY = 0;
568        charpos.m_bGlyphAdjust = FALSE;
569        if (pCIDFont == NULL) {
570            continue;
571        }
572        FX_WORD CID = pCIDFont->CIDFromCharCode(CharCode);
573        if (bVertWriting) {
574            charpos.m_OriginY = charpos.m_OriginX;
575            charpos.m_OriginX = 0;
576            short vx, vy;
577            pCIDFont->GetVertOrigin(CID, vx, vy);
578            charpos.m_OriginX -= FontSize * vx / 1000;
579            charpos.m_OriginY -= FontSize * vy / 1000;
580        }
581        FX_LPCBYTE pTransform = pCIDFont->GetCIDTransform(CID);
582        if (pTransform && !bVert) {
583            charpos.m_AdjustMatrix[0] = _CIDTransformToFloat(pTransform[0]);
584            charpos.m_AdjustMatrix[2] = _CIDTransformToFloat(pTransform[2]);
585            charpos.m_AdjustMatrix[1] = _CIDTransformToFloat(pTransform[1]);
586            charpos.m_AdjustMatrix[3] = _CIDTransformToFloat(pTransform[3]);
587            charpos.m_OriginX += _CIDTransformToFloat(pTransform[4]) * FontSize;
588            charpos.m_OriginY += _CIDTransformToFloat(pTransform[5]) * FontSize;
589            charpos.m_bGlyphAdjust = TRUE;
590        }
591    }
592}
593FX_BOOL CPDF_TextRenderer::DrawTextPath(CFX_RenderDevice* pDevice, int nChars, FX_DWORD* pCharCodes, FX_FLOAT* pCharPos,
594                                        CPDF_Font* pFont, FX_FLOAT font_size,
595                                        const CFX_AffineMatrix* pText2User, const CFX_AffineMatrix* pUser2Device,
596                                        const CFX_GraphStateData* pGraphState,
597                                        FX_ARGB fill_argb, FX_ARGB stroke_argb, CFX_PathData* pClippingPath, int nFlag)
598{
599    CFX_FontCache* pCache = pFont->m_pDocument ? pFont->m_pDocument->GetRenderData()->GetFontCache() : NULL;
600    CPDF_CharPosList CharPosList;
601    CharPosList.Load(nChars, pCharCodes, pCharPos, pFont, font_size);
602    return pDevice->DrawTextPath(CharPosList.m_nChars, CharPosList.m_pCharPos,
603                                 &pFont->m_Font, pCache, font_size, pText2User, pUser2Device,
604                                 pGraphState, fill_argb, stroke_argb, pClippingPath, nFlag);
605}
606void CPDF_TextRenderer::DrawTextString(CFX_RenderDevice* pDevice, int left, int top, CPDF_Font* pFont, int height,
607                                       const CFX_ByteString& str, FX_ARGB argb)
608{
609    FX_RECT font_bbox;
610    pFont->GetFontBBox(font_bbox);
611    FX_FLOAT font_size = (FX_FLOAT)height * 1000.0f / (FX_FLOAT)(font_bbox.top - font_bbox.bottom);
612    FX_FLOAT origin_x = (FX_FLOAT)left;
613    FX_FLOAT origin_y = (FX_FLOAT)top + font_size * (FX_FLOAT)font_bbox.top / 1000.0f;
614    CFX_AffineMatrix matrix(1.0f, 0, 0, -1.0f, 0, 0);
615    DrawTextString(pDevice, origin_x, origin_y, pFont, font_size, &matrix, str, argb);
616}
617void CPDF_TextRenderer::DrawTextString(CFX_RenderDevice* pDevice, FX_FLOAT origin_x, FX_FLOAT origin_y, CPDF_Font* pFont, FX_FLOAT font_size,
618                                       const CFX_AffineMatrix* pMatrix, const CFX_ByteString& str, FX_ARGB fill_argb,
619                                       FX_ARGB stroke_argb, const CFX_GraphStateData* pGraphState, const CPDF_RenderOptions* pOptions)
620{
621    int nChars = pFont->CountChar(str, str.GetLength());
622    if (nChars == 0) {
623        return;
624    }
625    FX_DWORD charcode;
626    int offset = 0;
627    FX_DWORD* pCharCodes;
628    FX_FLOAT* pCharPos;
629    if (nChars == 1) {
630        charcode = pFont->GetNextChar(str, offset);
631        pCharCodes = (FX_DWORD*)(FX_UINTPTR)charcode;
632        pCharPos = NULL;
633    } else {
634        pCharCodes = FX_Alloc(FX_DWORD, nChars);
635        pCharPos = FX_Alloc(FX_FLOAT, nChars - 1);
636        FX_FLOAT cur_pos = 0;
637        for (int i = 0; i < nChars; i ++) {
638            pCharCodes[i] = pFont->GetNextChar(str, offset);
639            if (i) {
640                pCharPos[i - 1] = cur_pos;
641            }
642            cur_pos += pFont->GetCharWidthF(pCharCodes[i]) * font_size / 1000;
643        }
644    }
645    CFX_AffineMatrix matrix;
646    if (pMatrix) {
647        matrix = *pMatrix;
648    }
649    matrix.e = origin_x;
650    matrix.f = origin_y;
651    if (pFont->GetFontType() == PDFFONT_TYPE3)
652        ;
653    else if (stroke_argb == 0) {
654        DrawNormalText(pDevice, nChars, pCharCodes, pCharPos, pFont, font_size, &matrix, fill_argb, pOptions);
655    } else
656        DrawTextPath(pDevice, nChars, pCharCodes, pCharPos, pFont, font_size, &matrix, NULL, pGraphState,
657                     fill_argb, stroke_argb, NULL);
658    if (nChars > 1) {
659        FX_Free(pCharCodes);
660        FX_Free(pCharPos);
661    }
662}
663FX_BOOL CPDF_TextRenderer::DrawNormalText(CFX_RenderDevice* pDevice, int nChars, FX_DWORD* pCharCodes, FX_FLOAT* pCharPos,
664        CPDF_Font* pFont, FX_FLOAT font_size,
665        const CFX_AffineMatrix* pText2Device,
666        FX_ARGB fill_argb, const CPDF_RenderOptions* pOptions)
667{
668    CFX_FontCache* pCache = pFont->m_pDocument ? pFont->m_pDocument->GetRenderData()->GetFontCache() : NULL;
669    CPDF_CharPosList CharPosList;
670    CharPosList.Load(nChars, pCharCodes, pCharPos, pFont, font_size);
671    int FXGE_flags = 0;
672    if (pOptions) {
673        FX_DWORD dwFlags = pOptions->m_Flags;
674        if (dwFlags & RENDER_CLEARTYPE) {
675            FXGE_flags |= FXTEXT_CLEARTYPE;
676            if (dwFlags & RENDER_BGR_STRIPE) {
677                FXGE_flags |= FXTEXT_BGR_STRIPE;
678            }
679        }
680        if (dwFlags & RENDER_NOTEXTSMOOTH) {
681            FXGE_flags |= FXTEXT_NOSMOOTH;
682        }
683        if (dwFlags & RENDER_PRINTGRAPHICTEXT) {
684            FXGE_flags |= FXTEXT_PRINTGRAPHICTEXT;
685        }
686        if (dwFlags & RENDER_NO_NATIVETEXT) {
687            FXGE_flags |= FXTEXT_NO_NATIVETEXT;
688        }
689        if (dwFlags & RENDER_PRINTIMAGETEXT) {
690            FXGE_flags |= FXTEXT_PRINTIMAGETEXT;
691        }
692    } else {
693        FXGE_flags = FXTEXT_CLEARTYPE;
694    }
695    if (pFont->GetFontType() & PDFFONT_CIDFONT) {
696        FXGE_flags |= FXFONT_CIDFONT;
697    }
698    return pDevice->DrawNormalText(CharPosList.m_nChars, CharPosList.m_pCharPos, &pFont->m_Font, pCache, font_size, pText2Device, fill_argb, FXGE_flags);
699}
700void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj, const CFX_AffineMatrix* pObj2Device,
701        CPDF_Font* pFont, FX_FLOAT font_size,
702        const CFX_AffineMatrix* pTextMatrix, FX_BOOL bFill, FX_BOOL bStroke)
703{
704    if (!bStroke) {
705        CPDF_PathObject path;
706        CPDF_TextObject* pCopy = FX_NEW CPDF_TextObject;
707        pCopy->Copy(textobj);
708        path.m_bStroke = FALSE;
709        path.m_FillType = FXFILL_WINDING;
710        path.m_ClipPath.AppendTexts(&pCopy, 1);
711        path.m_ColorState = textobj->m_ColorState;
712        path.m_Path.New()->AppendRect(textobj->m_Left, textobj->m_Bottom, textobj->m_Right, textobj->m_Top);
713        path.m_Left = textobj->m_Left;
714        path.m_Bottom = textobj->m_Bottom;
715        path.m_Right = textobj->m_Right;
716        path.m_Top = textobj->m_Top;
717        RenderSingleObject(&path, pObj2Device);
718        return;
719    }
720    CFX_FontCache* pCache;
721    if (pFont->m_pDocument) {
722        pCache = pFont->m_pDocument->GetRenderData()->GetFontCache();
723    } else {
724        pCache = CFX_GEModule::Get()->GetFontCache();
725    }
726    CFX_FaceCache* pFaceCache = pCache->GetCachedFace(&pFont->m_Font);
727    FX_FONTCACHE_DEFINE(pCache, &pFont->m_Font);
728    CPDF_CharPosList CharPosList;
729    CharPosList.Load(textobj->m_nChars, textobj->m_pCharCodes, textobj->m_pCharPos, pFont, font_size);
730    for (FX_DWORD i = 0; i < CharPosList.m_nChars; i ++) {
731        FXTEXT_CHARPOS& charpos = CharPosList.m_pCharPos[i];
732        const CFX_PathData* pPath = pFaceCache->LoadGlyphPath(&pFont->m_Font, charpos.m_GlyphIndex,
733                                    charpos.m_FontCharWidth);
734        if (pPath == NULL) {
735            continue;
736        }
737        CPDF_PathObject path;
738        path.m_GraphState = textobj->m_GraphState;
739        path.m_ColorState = textobj->m_ColorState;
740        CFX_AffineMatrix matrix;
741        if (charpos.m_bGlyphAdjust)
742            matrix.Set(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
743                       charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], 0, 0);
744        matrix.Concat(font_size, 0, 0, font_size, charpos.m_OriginX, charpos.m_OriginY);
745        path.m_Path.New()->Append(pPath, &matrix);
746        path.m_Matrix = *pTextMatrix;
747        path.m_bStroke = bStroke;
748        path.m_FillType = bFill ? FXFILL_WINDING : 0;
749        path.CalcBoundingBox();
750        ProcessPath(&path, pObj2Device);
751    }
752}
753CFX_PathData* CPDF_Font::LoadGlyphPath(FX_DWORD charcode, int dest_width)
754{
755    int glyph_index = GlyphFromCharCode(charcode);
756    if (m_Font.m_Face == NULL) {
757        return NULL;
758    }
759    return m_Font.LoadGlyphPath(glyph_index, dest_width);
760}
761