• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/fxge/win32/cfx_psrenderer.h"
8 
9 #include <memory>
10 #include <sstream>
11 
12 #include "core/fpdfapi/cpdf_modulemgr.h"
13 #include "core/fxcodec/codec/ccodec_basicmodule.h"
14 #include "core/fxcodec/codec/ccodec_faxmodule.h"
15 #include "core/fxcodec/codec/ccodec_flatemodule.h"
16 #include "core/fxcodec/codec/ccodec_jpegmodule.h"
17 #include "core/fxcodec/fx_codec.h"
18 #include "core/fxcrt/maybe_owned.h"
19 #include "core/fxge/cfx_facecache.h"
20 #include "core/fxge/cfx_fontcache.h"
21 #include "core/fxge/cfx_gemodule.h"
22 #include "core/fxge/cfx_pathdata.h"
23 #include "core/fxge/cfx_renderdevice.h"
24 #include "core/fxge/dib/cfx_dibextractor.h"
25 #include "core/fxge/win32/cpsoutput.h"
26 #include "third_party/base/ptr_util.h"
27 
28 namespace {
29 
FaxCompressData(uint8_t * src_buf,int width,int height,std::unique_ptr<uint8_t,FxFreeDeleter> * dest_buf,uint32_t * dest_size)30 void FaxCompressData(uint8_t* src_buf,
31                      int width,
32                      int height,
33                      std::unique_ptr<uint8_t, FxFreeDeleter>* dest_buf,
34                      uint32_t* dest_size) {
35   if (width * height > 128) {
36     CCodec_FaxModule::FaxEncode(src_buf, width, height, (width + 7) / 8,
37                                 dest_buf, dest_size);
38     FX_Free(src_buf);
39   } else {
40     dest_buf->reset(src_buf);
41     *dest_size = (width + 7) / 8 * height;
42   }
43 }
44 
PSCompressData(int PSLevel,uint8_t * src_buf,uint32_t src_size,uint8_t ** output_buf,uint32_t * output_size,const char ** filter)45 void PSCompressData(int PSLevel,
46                     uint8_t* src_buf,
47                     uint32_t src_size,
48                     uint8_t** output_buf,
49                     uint32_t* output_size,
50                     const char** filter) {
51   *output_buf = src_buf;
52   *output_size = src_size;
53   *filter = "";
54   if (src_size < 1024)
55     return;
56 
57   CCodec_ModuleMgr* pEncoders = CPDF_ModuleMgr::Get()->GetCodecModule();
58   uint8_t* dest_buf = nullptr;
59   uint32_t dest_size = src_size;
60   if (PSLevel >= 3) {
61     if (pEncoders->GetFlateModule()->Encode(src_buf, src_size, &dest_buf,
62                                             &dest_size)) {
63       *filter = "/FlateDecode filter ";
64     }
65   } else {
66     if (pEncoders->GetBasicModule()->RunLengthEncode(src_buf, src_size,
67                                                      &dest_buf, &dest_size)) {
68       *filter = "/RunLengthDecode filter ";
69     }
70   }
71   if (dest_size < src_size) {
72     *output_buf = dest_buf;
73     *output_size = dest_size;
74   } else {
75     *filter = nullptr;
76     FX_Free(dest_buf);
77   }
78 }
79 
80 }  // namespace
81 
82 struct PSGlyph {
83   UnownedPtr<CFX_Font> m_pFont;
84   uint32_t m_GlyphIndex;
85   bool m_bGlyphAdjust;
86   float m_AdjustMatrix[4];
87 };
88 
89 class CPSFont {
90  public:
91   PSGlyph m_Glyphs[256];
92   int m_nGlyphs;
93 };
94 
CFX_PSRenderer()95 CFX_PSRenderer::CFX_PSRenderer()
96     : m_pStream(nullptr),
97       m_bGraphStateSet(false),
98       m_bColorSet(false),
99       m_bInited(false) {}
100 
~CFX_PSRenderer()101 CFX_PSRenderer::~CFX_PSRenderer() {}
102 
Init(const RetainPtr<IFX_WriteStream> & pStream,int pslevel,int width,int height,bool bCmykOutput)103 void CFX_PSRenderer::Init(const RetainPtr<IFX_WriteStream>& pStream,
104                           int pslevel,
105                           int width,
106                           int height,
107                           bool bCmykOutput) {
108   m_PSLevel = pslevel;
109   m_pStream = pStream;
110   m_ClipBox.left = 0;
111   m_ClipBox.top = 0;
112   m_ClipBox.right = width;
113   m_ClipBox.bottom = height;
114   m_bCmykOutput = bCmykOutput;
115 }
116 
StartRendering()117 bool CFX_PSRenderer::StartRendering() {
118   if (m_bInited)
119     return true;
120 
121   static const char init_str[] =
122       "\nsave\n/im/initmatrix load def\n"
123       "/n/newpath load def/m/moveto load def/l/lineto load def/c/curveto load "
124       "def/h/closepath load def\n"
125       "/f/fill load def/F/eofill load def/s/stroke load def/W/clip load "
126       "def/W*/eoclip load def\n"
127       "/rg/setrgbcolor load def/k/setcmykcolor load def\n"
128       "/J/setlinecap load def/j/setlinejoin load def/w/setlinewidth load "
129       "def/M/setmiterlimit load def/d/setdash load def\n"
130       "/q/gsave load def/Q/grestore load def/iM/imagemask load def\n"
131       "/Tj/show load def/Ff/findfont load def/Fs/scalefont load def/Sf/setfont "
132       "load def\n"
133       "/cm/concat load def/Cm/currentmatrix load def/mx/matrix load "
134       "def/sm/setmatrix load def\n";
135   m_pStream->WriteString(init_str);
136   m_bInited = true;
137   return true;
138 }
139 
EndRendering()140 void CFX_PSRenderer::EndRendering() {
141   if (!m_bInited)
142     return;
143 
144   m_pStream->WriteString("\nrestore\n");
145   m_bInited = false;
146 }
147 
SaveState()148 void CFX_PSRenderer::SaveState() {
149   StartRendering();
150   m_pStream->WriteString("q\n");
151   m_ClipBoxStack.push_back(m_ClipBox);
152 }
153 
RestoreState(bool bKeepSaved)154 void CFX_PSRenderer::RestoreState(bool bKeepSaved) {
155   StartRendering();
156   m_pStream->WriteString("Q\n");
157   if (bKeepSaved)
158     m_pStream->WriteString("q\n");
159 
160   m_bColorSet = false;
161   m_bGraphStateSet = false;
162   if (m_ClipBoxStack.empty())
163     return;
164 
165   m_ClipBox = m_ClipBoxStack.back();
166   if (!bKeepSaved)
167     m_ClipBoxStack.pop_back();
168 }
169 
OutputPath(const CFX_PathData * pPathData,const CFX_Matrix * pObject2Device)170 void CFX_PSRenderer::OutputPath(const CFX_PathData* pPathData,
171                                 const CFX_Matrix* pObject2Device) {
172   std::ostringstream buf;
173   size_t size = pPathData->GetPoints().size();
174 
175   for (size_t i = 0; i < size; i++) {
176     FXPT_TYPE type = pPathData->GetType(i);
177     bool closing = pPathData->IsClosingFigure(i);
178     CFX_PointF pos = pPathData->GetPoint(i);
179     if (pObject2Device)
180       pos = pObject2Device->Transform(pos);
181 
182     buf << pos.x << " " << pos.y;
183     switch (type) {
184       case FXPT_TYPE::MoveTo:
185         buf << " m ";
186         break;
187       case FXPT_TYPE::LineTo:
188         buf << " l ";
189         if (closing)
190           buf << "h ";
191         break;
192       case FXPT_TYPE::BezierTo: {
193         CFX_PointF pos1 = pPathData->GetPoint(i + 1);
194         CFX_PointF pos2 = pPathData->GetPoint(i + 2);
195         if (pObject2Device) {
196           pos1 = pObject2Device->Transform(pos1);
197           pos2 = pObject2Device->Transform(pos2);
198         }
199         buf << " " << pos1.x << " " << pos1.y << " " << pos2.x << " " << pos2.y
200             << " c";
201         if (closing)
202           buf << " h";
203         buf << "\n";
204         i += 2;
205         break;
206       }
207     }
208   }
209   m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
210 }
211 
SetClip_PathFill(const CFX_PathData * pPathData,const CFX_Matrix * pObject2Device,int fill_mode)212 void CFX_PSRenderer::SetClip_PathFill(const CFX_PathData* pPathData,
213                                       const CFX_Matrix* pObject2Device,
214                                       int fill_mode) {
215   StartRendering();
216   OutputPath(pPathData, pObject2Device);
217   CFX_FloatRect rect = pPathData->GetBoundingBox();
218   if (pObject2Device)
219     rect = pObject2Device->TransformRect(rect);
220 
221   m_ClipBox.left = static_cast<int>(rect.left);
222   m_ClipBox.right = static_cast<int>(rect.left + rect.right);
223   m_ClipBox.top = static_cast<int>(rect.top + rect.bottom);
224   m_ClipBox.bottom = static_cast<int>(rect.bottom);
225 
226   m_pStream->WriteString("W");
227   if ((fill_mode & 3) != FXFILL_WINDING)
228     m_pStream->WriteString("*");
229   m_pStream->WriteString(" n\n");
230 }
231 
SetClip_PathStroke(const CFX_PathData * pPathData,const CFX_Matrix * pObject2Device,const CFX_GraphStateData * pGraphState)232 void CFX_PSRenderer::SetClip_PathStroke(const CFX_PathData* pPathData,
233                                         const CFX_Matrix* pObject2Device,
234                                         const CFX_GraphStateData* pGraphState) {
235   StartRendering();
236   SetGraphState(pGraphState);
237   if (pObject2Device) {
238     std::ostringstream buf;
239     buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " "
240         << pObject2Device->c << " " << pObject2Device->d << " "
241         << pObject2Device->e << " " << pObject2Device->f << "]cm ";
242     m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
243   }
244 
245   OutputPath(pPathData, nullptr);
246   CFX_FloatRect rect = pPathData->GetBoundingBox(pGraphState->m_LineWidth,
247                                                  pGraphState->m_MiterLimit);
248   m_ClipBox.Intersect(pObject2Device->TransformRect(rect).GetOuterRect());
249 
250   m_pStream->WriteString("strokepath W n");
251   if (pObject2Device)
252     m_pStream->WriteString(" sm");
253   m_pStream->WriteString("\n");
254 }
255 
DrawPath(const CFX_PathData * pPathData,const CFX_Matrix * pObject2Device,const CFX_GraphStateData * pGraphState,uint32_t fill_color,uint32_t stroke_color,int fill_mode)256 bool CFX_PSRenderer::DrawPath(const CFX_PathData* pPathData,
257                               const CFX_Matrix* pObject2Device,
258                               const CFX_GraphStateData* pGraphState,
259                               uint32_t fill_color,
260                               uint32_t stroke_color,
261                               int fill_mode) {
262   StartRendering();
263   int fill_alpha = FXARGB_A(fill_color);
264   int stroke_alpha = FXARGB_A(stroke_color);
265   if (fill_alpha && fill_alpha < 255)
266     return false;
267   if (stroke_alpha && stroke_alpha < 255)
268     return false;
269   if (fill_alpha == 0 && stroke_alpha == 0)
270     return false;
271 
272   if (stroke_alpha) {
273     SetGraphState(pGraphState);
274     if (pObject2Device) {
275       std::ostringstream buf;
276       buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " "
277           << pObject2Device->c << " " << pObject2Device->d << " "
278           << pObject2Device->e << " " << pObject2Device->f << "]cm ";
279       m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
280     }
281   }
282 
283   OutputPath(pPathData, stroke_alpha ? nullptr : pObject2Device);
284   if (fill_mode && fill_alpha) {
285     SetColor(fill_color);
286     if ((fill_mode & 3) == FXFILL_WINDING) {
287       if (stroke_alpha)
288         m_pStream->WriteString("q f Q ");
289       else
290         m_pStream->WriteString("f");
291     } else if ((fill_mode & 3) == FXFILL_ALTERNATE) {
292       if (stroke_alpha)
293         m_pStream->WriteString("q F Q ");
294       else
295         m_pStream->WriteString("F");
296     }
297   }
298 
299   if (stroke_alpha) {
300     SetColor(stroke_color);
301     m_pStream->WriteString("s");
302     if (pObject2Device)
303       m_pStream->WriteString(" sm");
304   }
305 
306   m_pStream->WriteString("\n");
307   return true;
308 }
309 
SetGraphState(const CFX_GraphStateData * pGraphState)310 void CFX_PSRenderer::SetGraphState(const CFX_GraphStateData* pGraphState) {
311   std::ostringstream buf;
312   if (!m_bGraphStateSet ||
313       m_CurGraphState.m_LineCap != pGraphState->m_LineCap) {
314     buf << pGraphState->m_LineCap << " J\n";
315   }
316   if (!m_bGraphStateSet ||
317       m_CurGraphState.m_DashCount != pGraphState->m_DashCount ||
318       memcmp(m_CurGraphState.m_DashArray, pGraphState->m_DashArray,
319              sizeof(float) * m_CurGraphState.m_DashCount)) {
320     buf << "[";
321     for (int i = 0; i < pGraphState->m_DashCount; ++i)
322       buf << pGraphState->m_DashArray[i] << " ";
323 
324     buf << "]" << pGraphState->m_DashPhase << " d\n";
325   }
326   if (!m_bGraphStateSet ||
327       m_CurGraphState.m_LineJoin != pGraphState->m_LineJoin) {
328     buf << pGraphState->m_LineJoin << " j\n";
329   }
330   if (!m_bGraphStateSet ||
331       m_CurGraphState.m_LineWidth != pGraphState->m_LineWidth) {
332     buf << pGraphState->m_LineWidth << " w\n";
333   }
334   if (!m_bGraphStateSet ||
335       m_CurGraphState.m_MiterLimit != pGraphState->m_MiterLimit) {
336     buf << pGraphState->m_MiterLimit << " M\n";
337   }
338   m_CurGraphState.Copy(*pGraphState);
339   m_bGraphStateSet = true;
340   if (buf.tellp())
341     m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
342 }
343 
SetDIBits(const RetainPtr<CFX_DIBSource> & pSource,uint32_t color,int left,int top)344 bool CFX_PSRenderer::SetDIBits(const RetainPtr<CFX_DIBSource>& pSource,
345                                uint32_t color,
346                                int left,
347                                int top) {
348   StartRendering();
349   CFX_Matrix matrix((float)(pSource->GetWidth()), 0.0f, 0.0f,
350                     -(float)(pSource->GetHeight()), (float)(left),
351                     (float)(top + pSource->GetHeight()));
352   return DrawDIBits(pSource, color, &matrix, 0);
353 }
354 
StretchDIBits(const RetainPtr<CFX_DIBSource> & pSource,uint32_t color,int dest_left,int dest_top,int dest_width,int dest_height,uint32_t flags)355 bool CFX_PSRenderer::StretchDIBits(const RetainPtr<CFX_DIBSource>& pSource,
356                                    uint32_t color,
357                                    int dest_left,
358                                    int dest_top,
359                                    int dest_width,
360                                    int dest_height,
361                                    uint32_t flags) {
362   StartRendering();
363   CFX_Matrix matrix((float)(dest_width), 0.0f, 0.0f, (float)(-dest_height),
364                     (float)(dest_left), (float)(dest_top + dest_height));
365   return DrawDIBits(pSource, color, &matrix, flags);
366 }
367 
DrawDIBits(const RetainPtr<CFX_DIBSource> & pSource,uint32_t color,const CFX_Matrix * pMatrix,uint32_t flags)368 bool CFX_PSRenderer::DrawDIBits(const RetainPtr<CFX_DIBSource>& pSource,
369                                 uint32_t color,
370                                 const CFX_Matrix* pMatrix,
371                                 uint32_t flags) {
372   StartRendering();
373   if ((pMatrix->a == 0 && pMatrix->b == 0) ||
374       (pMatrix->c == 0 && pMatrix->d == 0)) {
375     return true;
376   }
377   if (pSource->HasAlpha())
378     return false;
379 
380   int alpha = FXARGB_A(color);
381   if (pSource->IsAlphaMask() && (alpha < 255 || pSource->GetBPP() != 1))
382     return false;
383 
384   m_pStream->WriteString("q\n");
385 
386   std::ostringstream buf;
387   buf << "[" << pMatrix->a << " " << pMatrix->b << " " << pMatrix->c << " "
388       << pMatrix->d << " " << pMatrix->e << " " << pMatrix->f << "]cm ";
389 
390   int width = pSource->GetWidth();
391   int height = pSource->GetHeight();
392   buf << width << " " << height;
393 
394   if (pSource->GetBPP() == 1 && !pSource->GetPalette()) {
395     int pitch = (width + 7) / 8;
396     uint32_t src_size = height * pitch;
397     uint8_t* src_buf = FX_Alloc(uint8_t, src_size);
398     for (int row = 0; row < height; row++) {
399       const uint8_t* src_scan = pSource->GetScanline(row);
400       memcpy(src_buf + row * pitch, src_scan, pitch);
401     }
402 
403     std::unique_ptr<uint8_t, FxFreeDeleter> output_buf;
404     uint32_t output_size;
405     FaxCompressData(src_buf, width, height, &output_buf, &output_size);
406     if (pSource->IsAlphaMask()) {
407       SetColor(color);
408       m_bColorSet = false;
409       buf << " true[";
410     } else {
411       buf << " 1[";
412     }
413     buf << width << " 0 0 -" << height << " 0 " << height
414         << "]currentfile/ASCII85Decode filter ";
415 
416     if (output_buf.get() != src_buf) {
417       buf << "<</K -1/EndOfBlock false/Columns " << width << "/Rows " << height
418           << ">>/CCITTFaxDecode filter ";
419     }
420     if (pSource->IsAlphaMask())
421       buf << "iM\n";
422     else
423       buf << "false 1 colorimage\n";
424 
425     m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
426     WritePSBinary(output_buf.get(), output_size);
427     output_buf.release();
428   } else {
429     CFX_DIBExtractor source_extractor(pSource);
430     RetainPtr<CFX_DIBSource> pConverted = source_extractor.GetBitmap();
431     if (!pConverted)
432       return false;
433     switch (pSource->GetFormat()) {
434       case FXDIB_1bppRgb:
435       case FXDIB_Rgb32:
436         pConverted = pConverted->CloneConvert(FXDIB_Rgb);
437         break;
438       case FXDIB_8bppRgb:
439         if (pSource->GetPalette()) {
440           pConverted = pConverted->CloneConvert(FXDIB_Rgb);
441         }
442         break;
443       case FXDIB_1bppCmyk:
444         pConverted = pConverted->CloneConvert(FXDIB_Cmyk);
445         break;
446       case FXDIB_8bppCmyk:
447         if (pSource->GetPalette()) {
448           pConverted = pConverted->CloneConvert(FXDIB_Cmyk);
449         }
450         break;
451       default:
452         break;
453     }
454     if (!pConverted) {
455       m_pStream->WriteString("\nQ\n");
456       return false;
457     }
458 
459     int bpp = pConverted->GetBPP() / 8;
460     uint8_t* output_buf = nullptr;
461     size_t output_size = 0;
462     const char* filter = nullptr;
463     if ((m_PSLevel == 2 || flags & FXRENDER_IMAGE_LOSSY) &&
464         CCodec_JpegModule::JpegEncode(pConverted, &output_buf, &output_size)) {
465       filter = "/DCTDecode filter ";
466     }
467     if (!filter) {
468       int src_pitch = width * bpp;
469       output_size = height * src_pitch;
470       output_buf = FX_Alloc(uint8_t, output_size);
471       for (int row = 0; row < height; row++) {
472         const uint8_t* src_scan = pConverted->GetScanline(row);
473         uint8_t* dest_scan = output_buf + row * src_pitch;
474         if (bpp == 3) {
475           for (int col = 0; col < width; col++) {
476             *dest_scan++ = src_scan[2];
477             *dest_scan++ = src_scan[1];
478             *dest_scan++ = *src_scan;
479             src_scan += 3;
480           }
481         } else {
482           memcpy(dest_scan, src_scan, src_pitch);
483         }
484       }
485       uint8_t* compressed_buf;
486       uint32_t compressed_size;
487       PSCompressData(m_PSLevel, output_buf, output_size, &compressed_buf,
488                      &compressed_size, &filter);
489       if (output_buf != compressed_buf)
490         FX_Free(output_buf);
491 
492       output_buf = compressed_buf;
493       output_size = compressed_size;
494     }
495     buf << " 8[";
496     buf << width << " 0 0 -" << height << " 0 " << height << "]";
497     buf << "currentfile/ASCII85Decode filter ";
498     if (filter)
499       buf << filter;
500 
501     buf << "false " << bpp;
502     buf << " colorimage\n";
503     m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
504 
505     WritePSBinary(output_buf, output_size);
506     FX_Free(output_buf);
507   }
508   m_pStream->WriteString("\nQ\n");
509   return true;
510 }
511 
SetColor(uint32_t color)512 void CFX_PSRenderer::SetColor(uint32_t color) {
513   bool bCMYK = false;
514   if (bCMYK != m_bCmykOutput || !m_bColorSet || m_LastColor != color) {
515     std::ostringstream buf;
516     if (bCMYK) {
517       buf << FXSYS_GetCValue(color) / 255.0 << " "
518           << FXSYS_GetMValue(color) / 255.0 << " "
519           << FXSYS_GetYValue(color) / 255.0 << " "
520           << FXSYS_GetKValue(color) / 255.0 << " k\n";
521     } else {
522       buf << FXARGB_R(color) / 255.0 << " " << FXARGB_G(color) / 255.0 << " "
523           << FXARGB_B(color) / 255.0 << " rg\n";
524     }
525     if (bCMYK == m_bCmykOutput) {
526       m_bColorSet = true;
527       m_LastColor = color;
528     }
529     m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
530   }
531 }
532 
FindPSFontGlyph(CFX_FaceCache * pFaceCache,CFX_Font * pFont,const FXTEXT_CHARPOS & charpos,int * ps_fontnum,int * ps_glyphindex)533 void CFX_PSRenderer::FindPSFontGlyph(CFX_FaceCache* pFaceCache,
534                                      CFX_Font* pFont,
535                                      const FXTEXT_CHARPOS& charpos,
536                                      int* ps_fontnum,
537                                      int* ps_glyphindex) {
538   int i = 0;
539   for (const auto& pPSFont : m_PSFontList) {
540     for (int j = 0; j < pPSFont->m_nGlyphs; j++) {
541       if (pPSFont->m_Glyphs[j].m_pFont == pFont &&
542           pPSFont->m_Glyphs[j].m_GlyphIndex == charpos.m_GlyphIndex &&
543           ((!pPSFont->m_Glyphs[j].m_bGlyphAdjust && !charpos.m_bGlyphAdjust) ||
544            (pPSFont->m_Glyphs[j].m_bGlyphAdjust && charpos.m_bGlyphAdjust &&
545             (fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[0] -
546                   charpos.m_AdjustMatrix[0]) < 0.01 &&
547              fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[1] -
548                   charpos.m_AdjustMatrix[1]) < 0.01 &&
549              fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[2] -
550                   charpos.m_AdjustMatrix[2]) < 0.01 &&
551              fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[3] -
552                   charpos.m_AdjustMatrix[3]) < 0.01)))) {
553         *ps_fontnum = i;
554         *ps_glyphindex = j;
555         return;
556       }
557     }
558     ++i;
559   }
560 
561   if (m_PSFontList.empty() || m_PSFontList.back()->m_nGlyphs == 256) {
562     m_PSFontList.push_back(pdfium::MakeUnique<CPSFont>());
563     m_PSFontList.back()->m_nGlyphs = 0;
564     std::ostringstream buf;
565     buf << "8 dict begin/FontType 3 def/FontMatrix[1 0 0 1 0 0]def\n"
566            "/FontBBox[0 0 0 0]def/Encoding 256 array def 0 1 255{Encoding "
567            "exch/.notdef put}for\n"
568            "/CharProcs 1 dict def CharProcs begin/.notdef {} def end\n"
569            "/BuildGlyph{1 0 -10 -10 10 10 setcachedevice exch/CharProcs get "
570            "exch 2 copy known not{pop/.notdef}if get exec}bind def\n"
571            "/BuildChar{1 index/Encoding get exch get 1 index/BuildGlyph get "
572            "exec}bind def\n"
573            "currentdict end\n";
574     buf << "/X" << static_cast<uint32_t>(m_PSFontList.size() - 1)
575         << " exch definefont pop\n";
576     m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
577     buf.str("");
578   }
579 
580   *ps_fontnum = m_PSFontList.size() - 1;
581   CPSFont* pPSFont = m_PSFontList[*ps_fontnum].get();
582   int glyphindex = pPSFont->m_nGlyphs;
583   *ps_glyphindex = glyphindex;
584   pPSFont->m_Glyphs[glyphindex].m_GlyphIndex = charpos.m_GlyphIndex;
585   pPSFont->m_Glyphs[glyphindex].m_pFont = pFont;
586   pPSFont->m_Glyphs[glyphindex].m_bGlyphAdjust = charpos.m_bGlyphAdjust;
587   if (charpos.m_bGlyphAdjust) {
588     pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[0] = charpos.m_AdjustMatrix[0];
589     pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[1] = charpos.m_AdjustMatrix[1];
590     pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[2] = charpos.m_AdjustMatrix[2];
591     pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[3] = charpos.m_AdjustMatrix[3];
592   }
593   pPSFont->m_nGlyphs++;
594 
595   CFX_Matrix matrix;
596   if (charpos.m_bGlyphAdjust) {
597     matrix =
598         CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
599                    charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], 0, 0);
600   }
601   matrix.Concat(CFX_Matrix(1.0f, 0, 0, 1.0f, 0, 0));
602   const CFX_PathData* pPathData = pFaceCache->LoadGlyphPath(
603       pFont, charpos.m_GlyphIndex, charpos.m_FontCharWidth);
604   if (!pPathData)
605     return;
606 
607   CFX_PathData TransformedPath(*pPathData);
608   if (charpos.m_bGlyphAdjust)
609     TransformedPath.Transform(&matrix);
610 
611   std::ostringstream buf;
612   buf << "/X" << *ps_fontnum << " Ff/CharProcs get begin/" << glyphindex
613       << "{n ";
614   for (size_t p = 0; p < TransformedPath.GetPoints().size(); p++) {
615     CFX_PointF point = TransformedPath.GetPoint(p);
616     switch (TransformedPath.GetType(p)) {
617       case FXPT_TYPE::MoveTo: {
618         buf << point.x << " " << point.y << " m\n";
619         break;
620       }
621       case FXPT_TYPE::LineTo: {
622         buf << point.x << " " << point.y << " l\n";
623         break;
624       }
625       case FXPT_TYPE::BezierTo: {
626         CFX_PointF point1 = TransformedPath.GetPoint(p + 1);
627         CFX_PointF point2 = TransformedPath.GetPoint(p + 2);
628         buf << point.x << " " << point.y << " " << point1.x << " " << point1.y
629             << " " << point2.x << " " << point2.y << " c\n";
630         p += 2;
631         break;
632       }
633     }
634   }
635   buf << "f}bind def end\n";
636   buf << "/X" << *ps_fontnum << " Ff/Encoding get " << glyphindex << "/"
637       << glyphindex << " put\n";
638   m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
639 }
640 
DrawText(int nChars,const FXTEXT_CHARPOS * pCharPos,CFX_Font * pFont,const CFX_Matrix * pObject2Device,float font_size,uint32_t color)641 bool CFX_PSRenderer::DrawText(int nChars,
642                               const FXTEXT_CHARPOS* pCharPos,
643                               CFX_Font* pFont,
644                               const CFX_Matrix* pObject2Device,
645                               float font_size,
646                               uint32_t color) {
647   // Do not send zero or negative font sizes to printers. See crbug.com/767343.
648   if (font_size <= 0.0)
649     return true;
650 
651   if ((pObject2Device->a == 0 && pObject2Device->b == 0) ||
652       (pObject2Device->c == 0 && pObject2Device->d == 0)) {
653     return true;
654   }
655 
656   StartRendering();
657   int alpha = FXARGB_A(color);
658   if (alpha < 255)
659     return false;
660 
661   SetColor(color);
662   std::ostringstream buf;
663   buf << "q[" << pObject2Device->a << " " << pObject2Device->b << " "
664       << pObject2Device->c << " " << pObject2Device->d << " "
665       << pObject2Device->e << " " << pObject2Device->f << "]cm\n";
666 
667   CFX_FontCache* pCache = CFX_GEModule::Get()->GetFontCache();
668   CFX_FaceCache* pFaceCache = pCache->GetCachedFace(pFont);
669   int last_fontnum = -1;
670   for (int i = 0; i < nChars; i++) {
671     int ps_fontnum, ps_glyphindex;
672     FindPSFontGlyph(pFaceCache, pFont, pCharPos[i], &ps_fontnum,
673                     &ps_glyphindex);
674     if (last_fontnum != ps_fontnum) {
675       buf << "/X" << ps_fontnum << " Ff " << font_size << " Fs Sf ";
676       last_fontnum = ps_fontnum;
677     }
678     buf << pCharPos[i].m_Origin.x << " " << pCharPos[i].m_Origin.y << " m";
679     ByteString hex = ByteString::Format("<%02X>", ps_glyphindex);
680     buf << hex.AsStringView() << "Tj\n";
681   }
682   buf << "Q\n";
683   m_pStream->WriteBlock(buf.str().c_str(), buf.tellp());
684   pCache->ReleaseCachedFace(pFont);
685   return true;
686 }
687 
WritePSBinary(const uint8_t * data,int len)688 void CFX_PSRenderer::WritePSBinary(const uint8_t* data, int len) {
689   uint8_t* dest_buf;
690   uint32_t dest_size;
691   CCodec_ModuleMgr* pEncoders = CPDF_ModuleMgr::Get()->GetCodecModule();
692   if (pEncoders->GetBasicModule()->A85Encode(data, len, &dest_buf,
693                                              &dest_size)) {
694     m_pStream->WriteBlock(dest_buf, dest_size);
695     FX_Free(dest_buf);
696   } else {
697     m_pStream->WriteBlock(data, len);
698   }
699 }
700