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