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