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