NCBI C++ ToolKit
pdf.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: pdf.cpp 47292 2022-12-21 00:32:34Z evgeniev $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software / database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software / database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Peter Meric
27  *
28  * File Description:
29  * CPdf - Adobe PDF output
30  *
31  */
32 
33 
34 #include <ncbi_pch.hpp>
35 #include <gui/print/pdf.hpp>
36 #include <gui/print/pdf_object.hpp>
37 #include "page_handler.hpp"
38 #include "pdf_object_writer.hpp"
40 #include <gui/opengl/glpane.hpp>
42 #include <gui/opengl/glvbonode.hpp>
44 #include <gui/opengl/irender.hpp>
45 #include <gui/opengl/glutils.hpp>
47 
48 #include <corelib/ncbitime.hpp>
49 
50 #include <cmath>
51 
53 
54 
56  : m_ObjectWriter(new CPdfObjectWriter()),
57  m_ObjIdGenerator(new CIdGenerator()),
58  m_IsGreyScale(false),
59  m_PageDictionary(new CPdfDictionary()),
60  m_FontHandler(new CPdfFontHandler(m_ObjIdGenerator))
61 {
65  m_FontHandler));
66 }
67 
68 
70 {
71 }
72 
73 
74 void CPdf::SetOptions(const CPrintOptions& options)
75 {
77  m_PageHandler->SetOptions(m_Options);
78 }
79 
80 
82 {
84  m_ObjectWriter->SetOutputStream(ostream);
85 }
86 
87 
89 {
90  // version 1.6 (and beyond) provide support for page sizes > 200x200 inches
91  *m_Strm << "%PDF-1.7" << pdfeol;
92  // output binary characters in a comment to alert programs this is a binary file
93  *m_Strm << '%';
94  const unsigned int N = 10;
95  const char binchar[N] =
96  {
97  char(0x80), char(0x81), char(0x82), char(0x83), char(0x84),
98  char(0x85), char(0x86), char(0x87), char(0x88), char(0x89)
99  };
100  m_Strm->write(binchar, N);
101  *m_Strm << pdfeol;
102 
103  // create objects
107 
108  // populate info
109  CPdfObject& _info = *info;
110  _info["Author"] = new CPdfString("National Center for Biotechnology Information");
111  _info["Creator"] = new CPdfString("NCBI Genome Workbench http://www.ncbi.nlm.nih.gov/tools/gbench/");
112  _info["Producer"] = new CPdfString("NCBI PDF Generator");
113  _info["Title"] = new CPdfString(m_Options.GetTitle());
114  m_ObjectWriter->WriteObject(info);
115 
116  // populate catalog
118  catalog["Type"] = new CPdfName("Catalog");
119  catalog["Outlines"] = new CPdfIndirectObj(outlines);
120  catalog["Pages"] = new CPdfIndirectObj(m_PageHandler->GetObject());
121  catalog["PageLayout"] = new CPdfName(m_Options.GetNumPages() > 1 ? "OneColumn" : "SinglePage");
122  catalog["PageMode"] = new CPdfName("UseNone");
123  catalog["ViewerPreferences"] = new CPdfElement("<</DisplayDocTitle true>>");
124  if (m_Options.GetPrintOutput()) {
125  // Add print action to Catalog dictionary
126  catalog["OpenAction"] = new CPdfElement("<</S/Named /N/Print>>");
127  }
128  catalog["PageLabels"] = new CPdfElement("<</Nums[0<</S/D/P(Panel )>>]>>");
129  m_ObjectWriter->WriteObject(m_Catalog);
130 
131  // populate outlines
132  CPdfObject& ol = *outlines;
133  ol["Type"] = new CPdfName("Outlines");
134  ol["Count"] = new CPdfNumber(int(0));
135  m_ObjectWriter->WriteObject(outlines);
136 
137  // populate trailer
138  m_Trailer.Reset(new CPdfTrailer());
139  CPdfTrailer& trailer = *m_Trailer;
140  trailer["Info"] = new CPdfIndirectObj(info);
141  trailer["Root"] = new CPdfIndirectObj(m_Catalog);
142 
144  CPdfObject& fonts = *m_Fonts;
146  CRef<CPdfObject> font(*it);
147  const string fontname((*font)["Name"]->GetValue());
148  fonts[fontname] = new CPdfIndirectObj(font);
149  m_PrintInEndDoc.push_back(font);
150  }
151 
152  m_PrintInEndDoc.push_back(m_Fonts);
153 
154 
155  CRef<CPdfDictionary> resources(new CPdfDictionary());
156  CPdfDictionary& res = *resources;
157  res["Font"] = new CPdfIndirectObj(m_Fonts);
158 
159  (*m_PageDictionary)["Resources"] = resources;
160 }
161 
163 {
164  m_ObjectWriter->WriteObject(obj);
165 }
166 
168 {
170 
171  return m_CurrentContent;
172 }
173 
175 {
176  m_PageHandler->AddContent(m_CurrentContent);
177  //m_CurrentContent.Release();
178 }
179 
181 {
183 
184  return m_CurrentAnnot;
185 }
186 
188 {
189  m_PageHandler->AddAnnot(m_CurrentAnnot);
190 }
191 
193 {
195 
196  return m_CurrentReference;
197 }
198 
200 {
201  m_ObjectWriter->WriteObject(m_CurrentReference);
202 }
203 
205 {
206 }
207 
209 {
210  if ( !m_CurrentContent ) {
211  return;
212  }
213 
214  //m_PageHandler->AddContent(m_CurrentContent);
215  //m_PageCount += m_PageHandler->WritePages(m_PageCount + 1);
216  m_PageCount += m_PageHandler->WritePages();
217 }
218 
220  EFontFace face,
221  float font_size,
222  CVect2<float>& p,
223  const char* txt,
224  const CRgbaColor& c)
225 {
226  if (!m_CurrentContent.IsNull()) {
228 
229  string alpha_state = x_GetAlphaGraphicsState(c.GetAlpha());
230  if (alpha_state != "") {
231  m_CurrentContent->SetGraphicsState(alpha_state);
232  }
233 
234  float uu_inv = 1.0f/m_Options.GetUserUnit();
235  font_size *= uu_inv;
236 
237  m_CurrentContent->Text(font_handler, face, font_size, p, txt);
238  }
239  else {
240  LOG_POST("Unable to write text - current content object is NULL.");
241  }
242 }
243 
245  EFontFace face,
246  float font_size,
247  CMatrix3<double>& mat,
248  const char* txt,
249  const CRgbaColor& c)
250 {
251  if (!m_CurrentContent.IsNull()) {
253 
254  string alpha_state = x_GetAlphaGraphicsState(c.GetAlpha());
255  if (alpha_state != "") {
256  m_CurrentContent->SetGraphicsState(alpha_state);
257  }
258 
259  float uu_inv = 1.0f/m_Options.GetUserUnit();
260  font_size *= uu_inv;
261 
262  m_CurrentContent->Text(font_handler, face, font_size, mat, txt);
263  }
264  else {
265  LOG_POST("Unable to write text - current content object is NULL.");
266  }
267 }
268 
269 void CPdf::PrintModel(CGlPane& pane, CGlModel2D& model,
270  CRgbaGradColorTable* color_table)
271 {
272  vector<CGlVboNode*>& geom_nodes = model.GetNodes();
273 
275  for (size_t i=0; i<geom_nodes.size(); ++i) {
276  if (geom_nodes[i]->IsVisible() && !geom_nodes[i]->IsSkipped(eRenderPDF) ) {
277  if (geom_nodes[i]->GetState().ScaleInvarientSet() &&
278  geom_nodes[i]->GetState().GetScaleInvarient()) {
279  float uu_inv = 1.0f / GetOptions().GetUserUnit();
280  CVect2<double> scale(pane.GetScale()*(double)uu_inv);
281  if (geom_nodes[i]->GetState().GetScaleInvarient()) {
282  geom_nodes[i]->GetState().ScaleFactor(scale);
283  }
284  }
285  PrintBuffer(geom_nodes[i], pane.GetViewport(), color_table);
286  }
287  }
288 
289  vector<CGlVboNode*>& temp_geom_nodes = model.GetTempNodes();
290 
291  for (size_t i=0; i<temp_geom_nodes.size(); ++i) {
292  if (temp_geom_nodes[i]->IsVisible() && !temp_geom_nodes[i]->IsSkipped(eRenderPDF) ) {
293  if (temp_geom_nodes[i]->GetState().ScaleInvarientSet() &&
294  temp_geom_nodes[i]->GetState().GetScaleInvarient()) {
295  float uu_inv = 1.0f / GetOptions().GetUserUnit();
296  CVect2<double> scale(pane.GetScale()*(double)uu_inv);
297  if (temp_geom_nodes[i]->GetState().GetScaleInvarient()) {
298  temp_geom_nodes[i]->GetState().ScaleFactor(scale);
299  }
300  }
301  PrintBuffer(temp_geom_nodes[i], pane.GetViewport(), color_table);
302  }
303  }
304 }
305 
306 
307 void CPdf::AddTooltip(CGlPane& pane, const string& txt, CVect4<float>& rect)
308 {
309  CRef<CPdfObject> popup = BeginAnnot();
310 
311  // Project the coordinates of the label (model coordinates) onto
312  // viewport coordinates which are used for the popup.
313  TVPRect vp = pane.GetViewport();
314 
315  GLint viewport[4];
316  GLdouble model_view_matrix[16];
317  GLdouble projection_matrix[16];
318 
319  viewport[0] = GLint(vp.Left());
320  viewport[1] = GLint(vp.Bottom());
321  viewport[2] = GLint(vp.Width());
322  viewport[3] = GLint(vp.Height());
323 
324  IRender& gl = GetGl();
325  gl.GetModelViewMatrix(model_view_matrix);
326  gl.GetProjectionMatrix(projection_matrix);
327 
328  double llx = (double)rect.X();
329  double lly = (double)rect.Y();
330 
331  double urx = (double)rect.Z();
332  double ury = (double)rect.W();
333 
334  double px,py,px2,py2,dummyz;
335 
336  gluProjectX(llx, lly, 0.0,
337  model_view_matrix, projection_matrix, viewport,
338  &px, &py, &dummyz);
339 
340  string pos = " [" + NStr::NumericToString(int(px)) + " " +
341  NStr::NumericToString(int(py)) + " ";
342 
343  gluProjectX(urx, ury, 0.0,
344  model_view_matrix, projection_matrix, viewport,
345  &px2, &py2, &dummyz);
346 
347  pos += NStr::NumericToString(int(px2)) + " " +
348  NStr::NumericToString(int(py2)) + "]";
349 
350  // You still get an annoying little popup
351  CPdfDictionary& popup_dict = popup->GetDictionary();
352  popup_dict["Border"]= new CPdfElement(" [0 0 0]"); // colorloss annotation
353  //popup_dict["RD"]= new CPdfElement(" [ 2.0 2.0 2.0 2.0 ]");
354  popup_dict["Contents"] = new CPdfString(txt);
355  popup_dict["Type"] = new CPdfName("Annot");
356  popup_dict["Rect"] = new CPdfElement(pos);
357  popup_dict["Subtype"] = new CPdfName("Square");
358 
359  popup_dict["F"] = new CPdfNumber(64); // makes it read-only
360  EndAnnot();
361 
362 
363  // Where to put the popup box
364  string pos2 = " [" + NStr::NumericToString(int(px+50)) + " " +
365  NStr::NumericToString(int(py+50)) + " " +
366  NStr::NumericToString(int(px+300)) + " " +
367  NStr::NumericToString(int(py+200)) + "]";
368 
369  CRef<CPdfObject> annot = BeginAnnot();
370  CPdfDictionary& annot_dict = annot->GetDictionary();
371  popup_dict["Popup"] = new CPdfIndirectObj(annot);
372 
373  annot_dict["Parent"] = new CPdfIndirectObj(popup);
374  annot_dict["Subtype"] = new CPdfName("Popup");
375  annot_dict["Type"] = new CPdfName("Annot");
376  // This is where it initially pops up
377  annot_dict["Rect"] = new CPdfElement(pos2);
378  annot_dict["Open"] = new CPdfElement(" false");
379 
380  EndAnnot();
381 }
382 
383 
384 void CPdf::AddJSTooltip(CGlPane& pane, const string& txt, const string& title, CVect4<float>& rect)
385 {
386  CRef<CPdfObject> popup = BeginAnnot();
387 
388  // Project the coordinates of the label (model coordinates) onto
389  // viewport coordinates which are used for the popup.
390  TVPRect vp = pane.GetViewport();
391 
392  GLint viewport[4];
393  GLdouble model_view_matrix[16];
394  GLdouble projection_matrix[16];
395 
396  viewport[0] = GLint(vp.Left());
397  viewport[1] = GLint(vp.Bottom());
398  viewport[2] = GLint(vp.Width());
399  viewport[3] = GLint(vp.Height());
400 
401  IRender& gl = GetGl();
402  gl.GetModelViewMatrix(model_view_matrix);
403  gl.GetProjectionMatrix(projection_matrix);
404 
405  double llx = (double)rect.X();
406  double lly = (double)rect.Y();
407 
408  double urx = (double)rect.Z();
409  double ury = (double)rect.W();
410 
411  double px,py,dummyz;
412 
413  gluProjectX(llx, lly, 0.0,
414  model_view_matrix, projection_matrix, viewport,
415  &px, &py, &dummyz);
416 
417  string pos = " [" + NStr::NumericToString(int(px)) + " " +
418  NStr::NumericToString(int(py)) + " ";
419 
420  gluProjectX(urx, ury, 0.0,
421  model_view_matrix, projection_matrix, viewport,
422  &px, &py, &dummyz);
423 
424  pos += NStr::NumericToString(int(px)) + " " +
425  NStr::NumericToString(int(py)) + "]";
426  //pos = string(" [110.0 110.0 150.0 150.0]");
427 
428  // You still get an annoying little popup
429  CPdfDictionary& popup_dict = popup->GetDictionary();
430  //popup_dict["C"]= new CPdfElement(" [1 0 0 1.0]"); // red box
431  popup_dict["Border"]= new CPdfElement(" [0 0 0]"); // colorless annotation
432  popup_dict["Type"] = new CPdfName("Annot");
433  popup_dict["Rect"] = new CPdfElement(pos);
434  popup_dict["Subtype"] = new CPdfName("Link");
435  popup_dict["H"] = new CPdfName("I");
436 
437  CPdfDictionary* d = new CPdfDictionary();
438  (*d)["JS"] = new CPdfElement(" (app.alert\\(\"" + txt + "\", 3, 0, \"" + title + "\"\\))");
439  (*d)["S"] = new CPdfName("JavaScript");
440  (*d)["Type"] = new CPdfName("Action");
441  popup_dict["A"] = d;
442 
443  EndAnnot();
444 }
445 
446 void CPdf::PrintBuffer(CGlVboNode* node, const TVPRect& viewport,
447  CRgbaGradColorTable* color_table)
448 {
449  IRender& gl = GetGl();
450  gl.MatrixMode(GL_MODELVIEW);
451  gl.PushMatrix();
452 
453  for (size_t j=0; j<node->GetPositions().size(); ++j) {
455  mat.Transpose();
456  gl.LoadMatrixf(mat.GetData());
457 
458  if (node->GetDrawMode() == GL_LINES ||
459  node->GetDrawMode() == GL_LINE_STRIP ||
460  node->GetDrawMode() == GL_LINE_LOOP) {
461  PrintLineBuffer(node, viewport, color_table);
462  }
463  if (node->GetDrawMode() == GL_POINTS) {
464  PrintPointBuffer(node, viewport, color_table);
465  }
466  else if (node->GetDrawMode() == GL_TRIANGLES ||
467  node->GetDrawMode() == GL_TRIANGLE_FAN ||
468  node->GetDrawMode() == GL_TRIANGLE_STRIP) {
469  PrintTriBuffer(node, viewport, color_table);
470  }
471  else if (node->GetDrawMode() == GL_QUADS) {
472  PrintQuadBuffer(node, viewport, color_table);
473  }
474 
475  }
476 
477  gl.PopMatrix();
478 }
479 
480 bool CPdf::x_GetColors(CGlVboNode* node, vector<CRgbaColor>& colors,
481  CRgbaGradColorTable* color_table)
482 {
483  bool has_color = false;
484  IVboGeom::ESecondaryFormat colorFormat = node->GetSecondaryFormat();
485  if (colorFormat == IVboGeom::kSecondaryFormatColorFloat) {
486  has_color = true;
487  node->GetColorBuffer(colors);
488  }
489  else if (colorFormat == IVboGeom::kSecondaryFormatColorUChar) {
490  has_color = true;
491  vector<CVect4<unsigned char> > colors_uc;
492  colors.clear();
493  node->GetColorBufferUC(colors_uc);
494  for (size_t i=0; i<colors_uc.size(); ++i)
495  colors.push_back(CRgbaColor(colors_uc[i][0], colors_uc[i][1], colors_uc[i][2], colors_uc[i][3]));
496  }
497  // We may have stored colors in 1-dimensional texture buffer:
498  else if (color_table != NULL && colorFormat == IVboGeom::kSecondaryFormatTexture1D) {
499  vector<float> tex_coords;
500  node->GetTexCoordBuffer1D(tex_coords);
501  has_color = true;
502  for (size_t i=0; i<tex_coords.size(); ++i) {
503  CRgbaColor c = color_table->GetTexCoordColor(tex_coords[i]);
504  colors.push_back(c);
505  }
506  }
507 
508  // If we are in grey scale mode, make sure all colors are greyscale.
509  if (has_color && m_IsGreyScale) {
510  for (size_t i=0; i<colors.size(); ++i) {
511  colors[i] = colors[i].GetGreyscale();
512  }
513  }
514 
515  return has_color;
516 }
517 
519  CRgbaGradColorTable* color_table)
520 {
521  vector<CVect2<float> > vertices;
522  vector<CRgbaColor> colors;
523 
524  node->Get2DVertexBuffer(vertices);
525 
526  if (vertices.size() == 0)
527  return;
528 
529  GLint viewport[4];
530  GLdouble model_view_matrix[16];
531  GLdouble projection_matrix[16];
532 
533  // Get viewport from caller since Opengl value may be truncated
534  // since pdf page extents can in many cases be larger than the
535  // OpenGL maximum viewport size.
536  viewport[0] = GLint(vp.Left());
537  viewport[1] = GLint(vp.Bottom());
538  viewport[2] = GLint(vp.Width());
539  viewport[3] = GLint(vp.Height());
540 
541  IRender& gl = GetGl();
542  gl.GetModelViewMatrix(model_view_matrix);
543  gl.GetProjectionMatrix(projection_matrix);
544 
545  bool has_color = x_GetColors(node, colors, color_table);
546 
547  string alpha_state = x_GetAlphaGraphicsState(node);
548 
550 
551  /// Mostly do this for the clipping (push state) so that it doesn't stay active
552  content->PushGraphicsState();
553  content->SetClipBox(viewport[0], viewport[1], viewport[2], viewport[3]);
554 
555  // Use color buffer for color or, if none, use color in State, if set
556  CRgbaColor default_color;
557  if (!has_color && node->GetDefaultColor(default_color, m_IsGreyScale)) {
558  content->SetColor(default_color);
559  }
560 
561  float w = 1.0f;
562  if (node->GetState().PointSizeSet()) {
563  w = node->GetState().GetPointSize();
564  }
565  float uu_inv = 1.0f/m_Options.GetUserUnit();
566  w *= uu_inv;
567  content->SetLineWidth(w);
568 
569  content->SetLineCapStyle((int)CGlState::eButtCap);
571 
572  if (alpha_state != "") {
573  content->SetGraphicsState(alpha_state);
574  }
575 
576  // No actual point type in PDF. A line of 0 length with the right width
577  // and square caps (which are 1/2 the width on each end) should give
578  // the same results.
579  if (node->GetDrawMode() == GL_POINTS) {
580  for (size_t i=0; i<vertices.size(); i+=2) {
581  CVect2<float> v1 = vertices[i];
582  CVect2<float> v1p, v2p;
583  double px, py;
584  double dummyz;
585 
586  gluProjectX(v1.X(), v1.Y(), 0.0,
587  model_view_matrix, projection_matrix, viewport,
588  &px, &py, &dummyz);
589  v1p.X() = (float)px;
590  v1p.Y() = (float)py;
591  v2p = v1p;
592 
593  // Make length of line (in screen coordinates) same as point size
594  // to turn line into a point.
595  v2p.X() += w/2.0f;
596  v1p.X() -= w/2.0f;
597 
598  if (has_color) {
599  content->SetColor(colors[i]);
600  }
601 
602  content->Line(v1p, v2p);
603  /*
604  if (m_Options.GetMedia().Inside(v1p) &&
605  m_Options.GetMedia().Inside(v2p))
606  content->Line(v1p, v2p);
607  else
608  _TRACE("Geom outisde");
609  */
610  }
611  }
612 
613  content->PushGraphicsState();
614 
615  EndContent();
616 }
617 
618 void CPdf::PrintLineBuffer(CGlVboNode* node, const TVPRect& vp,
619  CRgbaGradColorTable* color_table)
620 {
621  vector<CVect2<float> > vertices;
622  vector<CRgbaColor> colors;
623 
624  node->Get2DVertexBuffer(vertices);
625 
626  if (vertices.size() == 0)
627  return;
628 
629  GLint viewport[4];
630  GLdouble model_view_matrix[16];
631  GLdouble projection_matrix[16];
632 
633  viewport[0] = GLint(vp.Left());
634  viewport[1] = GLint(vp.Bottom());
635  viewport[2] = GLint(vp.Width());
636  viewport[3] = GLint(vp.Height());
637 
638  IRender& gl = GetGl();
639  gl.GetModelViewMatrix(model_view_matrix);
640  gl.GetProjectionMatrix(projection_matrix);
641 
642  // Write out vertices...
644 
645  /// Mostly do this for the clipping (push state) so that it doesn't stay active
646  content->PushGraphicsState();
647  content->SetClipBox(viewport[0], viewport[1], viewport[2], viewport[3]);
648 
649  bool has_color = x_GetColors(node, colors, color_table);
650 
651  string alpha_state = x_GetAlphaGraphicsState(node);
652 
653  // Use color buffer for color or, if none, use color in State.
654  CRgbaColor default_color;
655  if (node->GetDefaultColor(default_color, m_IsGreyScale)) {
656  content->SetColor(default_color);
657  }
658 
659  if (node->GetState().LineWidthSet()) {
660  float w = node->GetState().GetLineWidth();
661  float uu_inv = 1.0f/m_Options.GetUserUnit();
662  w *= uu_inv;
663 
664  content->SetLineWidth(w);
665  }
666 
667  if (node->GetState().LineCapStyleSet()) {
668  content->SetLineCapStyle((int)node->GetState().GetLineCapStyle());
669  }
670 
671  if (node->GetState().LineJoinStyleSet()) {
672  content->SetLineJoinStyle((int)node->GetState().GetLineJoinStyle());
673  }
674 
675  if (node->GetState().IsEnabled(GL_LINE_STIPPLE) &&
676  node->GetState().LineStippleSet()) {
677  GLint factor;
678  GLushort pattern;
679 
680  node->GetState().GetLineStipple(factor, pattern);
681  content->SetLineDashStyle(factor, pattern);
682  }
683  else {
684  // explicitly set to 0 when not enabled to avoid accidental stippling
685  content->SetLineDashStyle(0, 0);
686  }
687 
688  if (alpha_state != "") {
689  content->SetGraphicsState(alpha_state);
690  }
691 
692  if (node->GetDrawMode() == GL_LINES) {
693  for (size_t i=0; i<vertices.size(); i+=2) {
694  if (i+1 >= vertices.size())
695  break;
696  CVect2<float> v1 = vertices[i];
697  CVect2<float> v2 = vertices[i+1];
698  CVect2<float> v1p;
699  CVect2<float> v2p;
700  double px, py;
701  double dummyz;
702 
703  gluProjectX(v1.X(), v1.Y(), 0.0,
704  model_view_matrix, projection_matrix, viewport,
705  &px, &py, &dummyz);
706  v1p.X() = (float)px;
707  v1p.Y() = (float)py;
708  gluProjectX(v2.X(), v2.Y(), 0.0,
709  model_view_matrix, projection_matrix, viewport,
710  &px, &py, &dummyz);
711  v2p.X() = (float)px;
712  v2p.Y() = (float)py;
713 
714  if (has_color) {
715  CRgbaColor c1 = colors[i];
716  CRgbaColor c2 = colors[i+1];
717 
718  // If ends are different colors, need to draw with a gradient.
719  // PDF has support for gradients with axial rendering and shfill
720  // https://partners.adobe.com/public/developer/en/ps/sdk/TN5600.SmoothShading.pdf
721  // but we have not currrently implmented support for these yet other
722  // than triangle shading.
723  float cdist = CRgbaColor::ColorDistance(c1, c2);
724  float dist = (v1p-v2p).Length();
725 
726  if (cdist > 0.1f && dist > 5.0f) {
727  // segment line based on color change and distance - for
728  // maximum color change (cdist=1) have a vertex every 5 pixels
729  float offset = 5.0f/cdist;
730  int num_segments = (int)ceilf(dist/offset); // so at least 1
731  float delta_pct = 1.0f/(float)num_segments;
732  float delta = 0.0f;
733  CVect2<float> seg_start = v1p;
734  CVect2<float> seg_end;
735 
736  for (int j=0; j<num_segments; ++j) {
737  CRgbaColor c = c1*(1.0f-delta) + c2*delta;
738  delta += delta_pct;
739  seg_end = v1p*(1.0f-delta) + v2p*delta;
740 
741  string current_alpha_state = x_GetAlphaGraphicsState(node, c.GetAlpha());
742  if (alpha_state != current_alpha_state) {
743  content->SetGraphicsState(current_alpha_state);
744  alpha_state = current_alpha_state;
745  }
746 
747  content->SetColor(c);
748  content->Line(seg_start, seg_end);
749 
750  seg_start = seg_end;
751  }
752 
753  }
754  else {
755  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[i].GetAlpha());
756  if (alpha_state != current_alpha_state) {
757  content->SetGraphicsState(current_alpha_state);
758  alpha_state = current_alpha_state;
759  }
760 
761  content->SetColor(colors[i]);
762  content->Line(v1p, v2p);
763  }
764  }
765  else {
766  content->Line(v1p, v2p);
767  }
768  }
769  }
770  else if (node->GetDrawMode() == GL_LINE_STRIP) {
771  for (size_t i=1; i<vertices.size(); i++) {
772  CVect2<float> v1 = vertices[i-1];
773  CVect2<float> v2 = vertices[i];
774  CVect2<float> v1p;
775  CVect2<float> v2p;
776  double px, py;
777  double dummyz;
778 
779  gluProjectX(v1.X(), v1.Y(), 0.0,
780  model_view_matrix, projection_matrix, viewport,
781  &px, &py, &dummyz);
782  v1p.X() = (float)px;
783  v1p.Y() = (float)py;
784  gluProjectX(v2.X(), v2.Y(), 0.0,
785  model_view_matrix, projection_matrix, viewport,
786  &px, &py, &dummyz);
787  v2p.X() = (float)px;
788  v2p.Y() = (float)py;
789 
790  if (has_color) {
791  content->SetColor(colors[i]);
792  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[i].GetAlpha());
793  if (alpha_state != current_alpha_state) {
794  content->SetGraphicsState(current_alpha_state);
795  alpha_state = current_alpha_state;
796  }
797  }
798 
799  content->Line(v1p, v2p);
800  }
801  }
802  else if (node->GetDrawMode() == GL_LINE_LOOP) {
803  for (size_t i=1; i<vertices.size(); i++) {
804  CVect2<float> v1 = vertices[i-1];
805  CVect2<float> v2 = vertices[i];
806  CVect2<float> v1p;
807  CVect2<float> v2p;
808  double px, py;
809  double dummyz;
810 
811  gluProjectX(v1.X(), v1.Y(), 0.0,
812  model_view_matrix, projection_matrix, viewport,
813  &px, &py, &dummyz);
814  v1p.X() = (float)px;
815  v1p.Y() = (float)py;
816  gluProjectX(v2.X(), v2.Y(), 0.0,
817  model_view_matrix, projection_matrix, viewport,
818  &px, &py, &dummyz);
819  v2p.X() = (float)px;
820  v2p.Y() = (float)py;
821 
822  if (has_color) {
823  content->SetColor(colors[i]);
824  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[i].GetAlpha());
825  if (alpha_state != current_alpha_state) {
826  content->SetGraphicsState(current_alpha_state);
827  alpha_state = current_alpha_state;
828  }
829  }
830 
831  content->Line(v1p, v2p);
832  }
833  CVect2<float> v1 = vertices[0];
834  CVect2<float> v2 = vertices[vertices.size()-1];
835  CVect2<float> v1p;
836  CVect2<float> v2p;
837  double px, py;
838  double dummyz;
839 
840  gluProjectX(v1.X(), v1.Y(), 0.0,
841  model_view_matrix, projection_matrix, viewport,
842  &px, &py, &dummyz);
843  v1p.X() = (float)px;
844  v1p.Y() = (float)py;
845  gluProjectX(v2.X(), v2.Y(), 0.0,
846  model_view_matrix, projection_matrix, viewport,
847  &px, &py, &dummyz);
848  v2p.X() = (float)px;
849  v2p.Y() = (float)py;
850 
851  content->Line(v1p, v2p);
852  }
853 
854  content->PopGraphicsState();
855 
856  EndContent();
857 }
858 
860  CRgbaGradColorTable* color_table)
861 {
862  vector<CVect2<float> > vertices;
863  vector<CRgbaColor> colors;
864 
865  node->Get2DVertexBuffer(vertices);
866 
867  if (vertices.size() == 0)
868  return;
869 
870  GLint viewport[4];
871  GLdouble model_view_matrix[16];
872  GLdouble projection_matrix[16];
873 
874  viewport[0] = GLint(vp.Left());
875  viewport[1] = GLint(vp.Bottom());
876  viewport[2] = GLint(vp.Width());
877  viewport[3] = GLint(vp.Height());
878 
879  IRender& gl = GetGl();
880  gl.GetModelViewMatrix(model_view_matrix);
881  gl.GetProjectionMatrix(projection_matrix);
882 
883  string alpha_state = x_GetAlphaGraphicsState(node);
884 
885  // Write out vertices...
887 
888  /// Mostly do this for the clipping (push state) so that it doesn't stay active
889  content->PushGraphicsState();
890  content->SetClipBox(viewport[0], viewport[1], viewport[2], viewport[3]);
891 
892  // Normally a color buffer would force ShadingType 4, but if user
893  // wants to force flat rendering would could wind up here in which case
894  // we can user the buffer colors per-triangle rather than per vertex
895  // (or per-perimeter)
896  bool has_color = x_GetColors(node, colors, color_table);
897 
898  bool flat_shading = (node->GetState().GetShadeModel() == GL_FLAT);
899 
900  // use color in State. Since triangles are flat, there is no color buffer
901  // (if we see a buffer we assume it is not flat)
902  CRgbaColor default_color;
903  if (node->GetDefaultColor(default_color, m_IsGreyScale)) {
904  content->SetColor(default_color);
905  content->SetFillColor(default_color);
906  }
907 
908  size_t i;
909  for (i=0; i<vertices.size(); ++i) {
910  CVect2<float> v1 = vertices[i];
911  CVect2<float> v1p;
912  double px, py;
913  double dummyz;
914 
915  gluProjectX(v1.X(), v1.Y(), 0.0,
916  model_view_matrix, projection_matrix, viewport,
917  &px, &py, &dummyz);
918  vertices[i].Set(float(px), float(py));
919  }
920 
921 
922  // GL_LINE or GL_FILL
923  bool fill = true;
924  if (node->GetState().GetPolygonMode() == GL_LINE) {
925  fill = false;
926 
927  if (node->GetState().LineCapStyleSet()) {
928  content->SetLineCapStyle((int)node->GetState().GetLineCapStyle());
929  }
930  if (node->GetState().LineJoinStyleSet()) {
931  content->SetLineJoinStyle((int)node->GetState().GetLineJoinStyle());
932  }
933  }
934 
935  if (alpha_state != "") {
936  content->SetGraphicsState(alpha_state);
937  }
938 
939  if (node->GetDrawMode() == GL_TRIANGLES) {
940 
941  CTriPerimeter tp;
942  for (i=0; i<vertices.size(); i+=3) {
943  if (i+2 >= vertices.size())
944  break;
945 
946  if (has_color && flat_shading) {
947  if (!(colors[i] == colors[0])) {
948  tp.Clear();
949  break;
950  }
951  }
952 
953  tp.AddTri(vertices[i], vertices[i+1], vertices[i+2]);
954  }
955 
956  // Can we convert this into a single perimeter?
957  vector<CVect2<float> > perimeter = tp.GetPerimiter();
958 
959  if (perimeter.size() > 2 && fill) {
960  if (has_color) {
961  content->SetFillColor(colors[0]);
962  content->SetColor(colors[0]);
963  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[0].GetAlpha());
964  if (alpha_state != current_alpha_state) {
965  content->SetGraphicsState(current_alpha_state);
966  alpha_state = current_alpha_state;
967  }
968  }
969 
970  content->Poly(perimeter);
971  }
972  else {
973  for (i=0; i<vertices.size(); i+=3) {
974  if (i+2 >= vertices.size())
975  break;
976  CVect2<float> v1p = vertices[i];
977  CVect2<float> v2p = vertices[i+1];
978  CVect2<float> v3p = vertices[i+2];
979 
980  if (has_color) {
981  content->SetFillColor(colors[i]);
982  content->SetColor(colors[i]);
983  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[i].GetAlpha());
984  if (alpha_state != current_alpha_state) {
985  content->SetGraphicsState(current_alpha_state);
986  alpha_state = current_alpha_state;
987  }
988  }
989 
990  if (fill) {
991  content->Tri(v1p, v2p, v3p);
992  }
993  else {
994  content->Line(v1p, v2p);
995  content->Line(v2p, v3p);
996  content->Line(v3p, v1p);
997  }
998  }
999  }
1000  }
1001  else if (node->GetDrawMode() == GL_TRIANGLE_STRIP) {
1002 
1003  CTriPerimeter tp;
1004  for (size_t j=2; j<vertices.size(); ++j) {
1005 
1006  if (has_color && flat_shading) {
1007  if (!(colors[j] == colors[0])) {
1008  tp.Clear();
1009  break;
1010  }
1011  }
1012 
1013  tp.AddTri(vertices[j], vertices[j-1], vertices[j-2]);
1014  }
1015 
1016  // Can we convert this into a single perimeter?
1017  vector<CVect2<float> > perimeter = tp.GetPerimiter();
1018 
1019  if (perimeter.size() > 2 && fill) {
1020  if (has_color) {
1021  content->SetFillColor(colors[0]);
1022  content->SetColor(colors[0]);
1023  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[0].GetAlpha());
1024  if (alpha_state != current_alpha_state) {
1025  content->SetGraphicsState(current_alpha_state);
1026  alpha_state = current_alpha_state;
1027  }
1028  }
1029 
1030  content->Poly(perimeter);
1031  }
1032  else {
1033  for (size_t j=2; j<vertices.size(); ++j) {
1034  CVect2<float> v1p = vertices[j];
1035  CVect2<float> v2p = vertices[j-1];
1036  CVect2<float> v3p = vertices[j-2];
1037 
1038  if (has_color) {
1039  content->SetFillColor(colors[j]);
1040  content->SetColor(colors[j]);
1041  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[j].GetAlpha());
1042  if (alpha_state != current_alpha_state) {
1043  content->SetGraphicsState(current_alpha_state);
1044  alpha_state = current_alpha_state;
1045  }
1046  }
1047  if (fill) {
1048  content->Tri(v1p, v2p, v3p);
1049  }
1050  else {
1051  content->Line(v1p, v2p);
1052  content->Line(v2p, v3p);
1053  content->Line(v3p, v1p);
1054  }
1055  }
1056  }
1057  }
1058  else if (node->GetDrawMode() == GL_TRIANGLE_FAN) {
1059  CVect2<float> v1p = vertices[0];
1060 
1061  vector<CVect2<float> > perimeter;
1062  for (size_t j=1; j<vertices.size(); ++j) {
1063  perimeter.push_back(vertices[j]);
1064  }
1065 
1066  // would user maybe want perimiter for edges? The could have always
1067  // just drawn edges for that, rather than tris.
1068  if (fill) {
1069  if (has_color) {
1070  content->SetFillColor(colors[0]);
1071  content->SetColor(colors[0]);
1072  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[0].GetAlpha());
1073  if (alpha_state != current_alpha_state) {
1074  content->SetGraphicsState(current_alpha_state);
1075  alpha_state = current_alpha_state;
1076  }
1077  }
1078 
1079  content->Poly(perimeter);
1080  }
1081  else {
1082  for (size_t j=2; j<vertices.size(); ++j) {
1083  CVect2<float> v2p = vertices[j-1];
1084  CVect2<float> v3p = vertices[j];
1085 
1086  if (has_color) {
1087  content->SetFillColor(colors[i]);
1088  content->SetColor(colors[i]);
1089  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[i].GetAlpha());
1090  if (alpha_state != current_alpha_state) {
1091  content->SetGraphicsState(current_alpha_state);
1092  alpha_state = current_alpha_state;
1093  }
1094  }
1095  else {
1096  content->Line(v1p, v2p);
1097  content->Line(v2p, v3p);
1098  content->Line(v3p, v1p);
1099  }
1100  }
1101  }
1102  }
1103 
1104  content->PopGraphicsState();
1105 
1106  EndContent();
1107 }
1108 
1110  const TVPRect& vp,
1111  CRgbaGradColorTable* color_table,
1112  CPdfObject::EBitCount bit_count)
1113 {
1114  // Iterate over all vertices and find the min/max values that capture the range
1115  // for this shading object (set of shaded triangles). You can give the pdf file
1116  // separate ranges for x and y but we will leave that out for now since most data
1117  // is clustered near the origin (x and y ranges are similar)
1118  if (node == NULL)
1119  return "";
1120 
1121  vector<CVect2<float> > vertices;
1122 
1123  node->Get2DVertexBuffer(vertices);
1124 
1125  if (vertices.size() < 3)
1126  return "";
1127 
1128  float minx = std::numeric_limits<float>::max();
1129  float miny = std::numeric_limits<float>::max();
1130 
1131  float maxx = -std::numeric_limits<float>::max();
1132  float maxy = -std::numeric_limits<float>::max();
1133 
1134  for (size_t v=0; v<vertices.size(); ++v) {
1135  minx = std::min(vertices[v].X(), minx);
1136  miny = std::min(vertices[v].Y(), miny);
1137  maxx = std::max(vertices[v].X(), maxx);
1138  maxy = std::max(vertices[v].Y(), maxy);
1139  }
1140 
1141  // Can't have a 0 or near 0 range size (the range is used in the pdf file
1142  // to set up values to convert the floating point values to integers in
1143  // a larger range so a little larger is no problem (see ShadingType 4, Decode)
1144  return AddShadedTris(node, vp, bit_count, color_table,
1145  int(minx-10.0f), int(maxx+10.0f),
1146  int(miny-10.0f), int(maxy+10.0f));
1147 }
1148 
1150  const TVPRect& /*vp*/,
1151  CPdfObject::EBitCount bit_count,
1152  CRgbaGradColorTable* color_table,
1153  int range_minx, int range_maxx,
1154  int range_miny, int range_maxy)
1155 {
1156  if (node == NULL)
1157  return "";
1158 
1159  vector<CVect2<float> > vertices;
1160  vector<CRgbaColor> colors;
1161 
1162  node->Get2DVertexBuffer(vertices);
1163 
1164  if (vertices.size() < 3)
1165  return "";
1166 
1167  CRgbaColor c;
1168  node->GetDefaultColor(c, m_IsGreyScale);
1169 
1170  CRgbaColor c1 = c;
1171  CRgbaColor c2 = c;
1172  CRgbaColor c3 = c;
1173 
1174  bool has_color = x_GetColors(node, colors, color_table);
1175 
1177  tris->StartTris(CPdfObject::eASCIIHex, bit_count, range_minx, range_maxx,
1178  range_miny, range_maxy);
1179 
1180 
1181  if (node->GetDrawMode() == GL_TRIANGLES) {
1182  for (size_t v=0; v+2<vertices.size(); v+=3) {
1183  if (has_color) {
1184  c1 = colors[v];
1185  c2 = colors[v+1];
1186  c3 = colors[v+2];
1187  }
1188  tris->Tri(0, vertices[v], c1, vertices[v+1], c2, vertices[v+2], c3);
1189  }
1190  }
1191  else if (node->GetDrawMode() == GL_TRIANGLE_STRIP) {
1192  if (has_color) {
1193  c1 = colors[0];
1194  c2 = colors[1];
1195  c3 = colors[2];
1196  }
1197  tris->Tri(0, vertices[0], c1, vertices[1], c2, vertices[2], c3);
1198 
1199  for (size_t v=3; v<vertices.size(); ++v) {
1200  if (has_color) {
1201  c1 = colors[v];
1202  }
1203  tris->Tri(1, vertices[v], c1);
1204  }
1205  }
1206  else if (node->GetDrawMode() == GL_TRIANGLE_FAN) {
1207  if (has_color) {
1208  c1 = colors[0];
1209  c2 = colors[1];
1210  c3 = colors[2];
1211  }
1212  tris->Tri(0, vertices[0], c1, vertices[1], c2, vertices[2], c3);
1213 
1214  for (size_t v=3; v<vertices.size(); ++v) {
1215  if (has_color) {
1216  c1 = colors[v];
1217  }
1218  tris->Tri(2, vertices[v], c1);
1219  }
1220  }
1221 
1222  tris->EndTris();
1223  EndReference();
1224 
1225  CRef<CPdfElement>& res = (*PageDictionary())["Resources"];
1226  if ( !res ) {
1227  res.Reset(new CPdfDictionary());
1228  }
1229 
1230  CRef<CPdfDictionary> _res(dynamic_cast <CPdfDictionary*>(res.GetPointer()));
1231  CRef<CPdfElement>& shaders = (*_res)["Shading"];
1232  if ( !shaders ) {
1233  shaders.Reset(new CPdfDictionary());
1234  }
1235 
1236  CRef<CPdfDictionary> _shaders(dynamic_cast <CPdfDictionary*>(shaders.GetPointer()));
1237 
1238  string shader_id = _shaders->GenShaderID();
1239  (*_shaders)[shader_id.c_str()] = new CPdfIndirectObj(tris);
1240 
1241  return shader_id;
1242 }
1243 
1244 void CPdf::RenderShaderInstance(CGlVboNode* node, const string& shader_id, const TVPRect& vp)
1245 {
1246  GLint viewport[4];
1247  GLdouble model_view_matrix[16];
1248  GLdouble projection_matrix[16];
1249 
1250  viewport[0] = GLint(vp.Left());
1251  viewport[1] = GLint(vp.Bottom());
1252  viewport[2] = GLint(vp.Width());
1253  viewport[3] = GLint(vp.Height());
1254 
1255  IRender& gl = GetGl();
1256  gl.GetModelViewMatrix(model_view_matrix);
1257  gl.GetProjectionMatrix(projection_matrix);
1258 
1259  CMatrix3<double> mat, matvt, matvs;
1260  matvt.Identity();
1261  matvs.Identity();
1262 
1263  // Compute modelview projection matrix and then get
1264  // homogeneous 2D (3x3) matrix for PDF from that result (ignore z)
1265  CMatrix4<double> mp(projection_matrix);
1266  CMatrix4<double> mm(model_view_matrix);
1267  mp.Transpose();
1268  mm.Transpose();
1269  CMatrix4<double> mvp = mp*mm;
1270  CMatrix3<double> mvp33 = CMatrix3<double>(mvp(0, 0), mvp(0, 1), mvp(0, 3),
1271  mvp(1, 0), mvp(1, 1), mvp(1, 3),
1272  mvp(3, 0), mvp(3, 1), mvp(3, 3));
1273 
1274 
1275  matvs(0,0) = ((double)viewport[2])/2.0;
1276  matvs(1,1) = ((double)viewport[3])/2.0;
1277 
1278  matvt(0,2) = matvs(0,0) + double(viewport[0]);
1279  matvt(1,2) = matvs(1,1) + double(viewport[1]);
1280 
1281  mat = matvt*matvs*mvp33;
1282 
1283  // can't do alpha in tris vertex-by-vertex, but if there is a color array, use alpha from there,
1284  // but only from the first element in the array!
1285  string alpha_state = x_GetAlphaGraphicsState(node);
1286 
1288 
1289  /// Mostly do this for the clipping (push state) so that it doesn't stay active
1290  shade_obj->PushGraphicsState();
1291  shade_obj->SetClipBox(viewport[0], viewport[1], viewport[2], viewport[3]);
1292 
1293  //(*shade_obj) << CPdfElement("q") << pdfbrk;
1294  (*shade_obj) << CPdfTransform(mat) << '\n';
1295  if (alpha_state != "")
1296  shade_obj->SetGraphicsState(alpha_state);
1297  (*shade_obj) << "/" << shader_id << " sh" << '\n';
1298  //(*shade_obj) << CPdfElement("Q") << pdfbrk;
1299 
1300  shade_obj->PopGraphicsState();
1301  EndContent();
1302 }
1303 
1304 void CPdf::PrintTriBuffer(CGlVboNode* node, const TVPRect& vp,
1305  CRgbaGradColorTable* color_table)
1306 {
1307  // decide if the triangles are gouraud (diff colors for each
1308  // vertex) or not. If the shading state is flat, or the color
1309  // buffer is empty, we have flat shaded tris.
1310 
1311  // Another issue with pdf is that a region tiled together out
1312  // of many polygons will show cracks if it is done using flat polygons -
1313  // only shaded polys seem to be able to join to hide cracks. The
1314  // alternative for flat is to provide the convex hull of the poly,
1315  // but we can't just do that since we don't know if it is convex.
1316  bool shaded = true;
1317 
1318  if (node->GetState().GetPolygonMode() == GL_LINE ||
1319  (node->GetState().GetShadeModel() == GL_FLAT &&
1320  node->GetState().GetPdfShadeStyle() == CGlState::eFlat)) {
1321  shaded = false;
1322  }
1323 
1324  if (shaded && !m_Options.GetGouraudShadingDisabled()) {
1325  string id = AddShadedTris(node, vp, color_table);
1326  if (id != "")
1327  RenderShaderInstance(node, id, vp);
1328  }
1329  else {
1330  x_PrintFlatTriBuffer(node, vp, color_table);
1331  }
1332 }
1333 
1335  CRgbaGradColorTable* color_table)
1336 {
1337  vector<CVect2<float> > vertices;
1338  vector<CRgbaColor> colors;
1339 
1340  node->Get2DVertexBuffer(vertices);
1341 
1342  if (vertices.size() == 0)
1343  return;
1344 
1345  GLint viewport[4];
1346  GLdouble model_view_matrix[16];
1347  GLdouble projection_matrix[16];
1348 
1349  viewport[0] = GLint(vp.Left());
1350  viewport[1] = GLint(vp.Bottom());
1351  viewport[2] = GLint(vp.Width());
1352  viewport[3] = GLint(vp.Height());
1353 
1354  IRender& gl = GetGl();
1355  gl.GetModelViewMatrix(model_view_matrix);
1356  gl.GetProjectionMatrix(projection_matrix);
1357 
1358  bool has_color = x_GetColors(node, colors, color_table);
1359  if (has_color) {
1360 
1361  if (node->GetState().GetShadeModel() != GL_FLAT) {
1362  // Warn we see that there is a
1363  // color buffer and that the colors in the buffer are not all the
1364  // same for the vertices of at least one quad.
1365  for (size_t i=3; i<colors.size(); i+=4) {
1366  if (!(colors[i] == colors[i-3]) ||
1367  !(colors[i] == colors[i-2]) ||
1368  !(colors[i] == colors[i-1])) {
1369  LOG_POST(Warning << "Warning - printing quads flat while \
1370  OpenGL state is shaded (only tris may be shaded)");
1371  break;
1372  }
1373  }
1374  }
1375  }
1376 
1377  string alpha_state = x_GetAlphaGraphicsState(node);
1378 
1379  // Start new pdf object
1381 
1382  /// Mostly do this for the clipping (push state) so that it doesn't stay active
1383  content->PushGraphicsState();
1384  content->SetClipBox(viewport[0], viewport[1], viewport[2], viewport[3]);
1385 
1386  // use color in State. Since triangles are flat, there is no color buffer
1387  // (if we see a buffer we assume it is not flat)
1388  CRgbaColor c;
1389  if (node->GetDefaultColor(c, m_IsGreyScale)) {
1390  content->SetColor(c);
1391  content->SetFillColor(c);
1392  }
1393 
1394  // GL_LINE or GL_FILL
1395  bool fill = true;
1396  if (node->GetState().GetPolygonMode() == GL_LINE) {
1397  fill = false;
1398 
1399  if (node->GetState().LineCapStyleSet()) {
1400  content->SetLineCapStyle((int)node->GetState().GetLineCapStyle());
1401  }
1402  if (node->GetState().LineJoinStyleSet()) {
1403  content->SetLineJoinStyle((int)node->GetState().GetLineJoinStyle());
1404  }
1405  }
1406 
1407  if (alpha_state != "") {
1408  content->SetGraphicsState(alpha_state);
1409  }
1410 
1411  for (size_t i=3; i<vertices.size(); i+=4) {
1412  CVect2<float> v1 = vertices[i-3];
1413  CVect2<float> v2 = vertices[i-2];
1414  CVect2<float> v3 = vertices[i-1];
1415  CVect2<float> v4 = vertices[i];
1416  CVect2<float> v1p;
1417  CVect2<float> v2p;
1418  CVect2<float> v3p;
1419  CVect2<float> v4p;
1420  double px, py;
1421  double dummyz;
1422 
1423  gluProjectX(v1.X(), v1.Y(), 0.0,
1424  model_view_matrix, projection_matrix, viewport,
1425  &px, &py, &dummyz);
1426  v1p.X() = (float)px;
1427  v1p.Y() = (float)py;
1428  gluProjectX(v2.X(), v2.Y(), 0.0,
1429  model_view_matrix, projection_matrix, viewport,
1430  &px, &py, &dummyz);
1431  v2p.X() = (float)px;
1432  v2p.Y() = (float)py;
1433  gluProjectX(v3.X(), v3.Y(), 0.0,
1434  model_view_matrix, projection_matrix, viewport,
1435  &px, &py, &dummyz);
1436  v3p.X() = (float)px;
1437  v3p.Y() = (float)py;
1438  gluProjectX(v4.X(), v4.Y(), 0.0,
1439  model_view_matrix, projection_matrix, viewport,
1440  &px, &py, &dummyz);
1441  v4p.X() = (float)px;
1442  v4p.Y() = (float)py;
1443 
1444  if (has_color) {
1445  content->SetFillColor(colors[i]);
1446  content->SetColor(colors[i]);
1447 
1448  string current_alpha_state = x_GetAlphaGraphicsState(node, colors[i].GetAlpha());
1449  if (alpha_state != current_alpha_state) {
1450  content->SetGraphicsState(current_alpha_state);
1451  alpha_state = current_alpha_state;
1452  }
1453  }
1454 
1455  if (fill) {
1456  content->Quad(v1p, v2p, v3p, v4p);
1457  }
1458  else {
1459  content->Line(v1p, v2p);
1460  content->Line(v2p, v3p);
1461  content->Line(v3p, v4p);
1462  content->Line(v4p, v1p);
1463  }
1464  }
1465 
1466  content->PopGraphicsState();
1467 
1468  EndContent();
1469 }
1470 
1472 {
1473  m_PageHandler->WritePageTree(m_PageDictionary);
1474 
1475  ITERATE(vector<TPdfObjectRef>, it, m_PrintInEndDoc) {
1476  m_ObjectWriter->WriteObject(*it);
1477  }
1478 
1479  // the number of objects is defined as one more than the highest
1480  // object number
1481  const unsigned int num_objects = m_ObjIdGenerator->NextId();
1482 
1483  // write the cross reference
1484  const CT_POS_TYPE xref_start = m_ObjectWriter->WriteXRef(num_objects);
1485 
1486  // write the trailer
1487  (*m_Trailer)["Size"] = new CPdfNumber(num_objects);
1488  *m_Strm << *m_Trailer;
1489 
1490  // output the location of the last xref section
1491  *m_Strm << "startxref" << endl << (xref_start - CT_POS_TYPE(0)) << endl;
1492 
1493  *m_Strm << "%%EOF" << pdfeol;
1494 }
1495 
1497 {
1498  size_t vertex_buf_size = node->GetVertexCount();
1499  if (vertex_buf_size == 0)
1500  return "";
1501 
1502  float alpha = 1.0f;
1503 
1504  // Only support most standard (alpha/one-minus-alpha) blending. If that is
1505  // not the mode set in OpenGL do not have blending. (It is the default too
1506  // so if blending is enabled but function not set, we enable it).
1507  if ((node->GetState().IsEnabled(GL_BLEND) &&
1508  !node->GetState().BlendFuncSet()) ||
1509  (node->GetState().GetSourceBlendFactor() == GL_SRC_ALPHA &&
1510  node->GetState().GetTargetBlendFactor() == GL_ONE_MINUS_SRC_ALPHA)) {
1511 
1512  // First get alpha value from color in state
1513  if (node->GetState().ColorSet())
1514  alpha = node->GetState().GetColor().GetAlpha();
1515 
1516  // Then check for color (and alpha) in the color buffer. Note that this takes alpha
1517  // from the FIRST COLOR. If other vertices have different alpha values we will not
1518  // capture that.
1520  vector<CRgbaColor> colors;
1521  node->GetColorBuffer(colors);
1522  alpha = colors[0].GetAlpha();
1523  }
1524  }
1525 
1526  string state_name = x_GetAlphaGraphicsState(node, alpha);
1527 
1528  return state_name;
1529 }
1530 
1531 
1532 string CPdf::x_GetAlphaGraphicsState(CGlVboNode* node, float alpha)
1533 {
1534  string state_name;
1535 
1536  // If blending is not enabled or enabled but set to a blending function
1537  // other than src_alpha, one_minus_src_alpha, do not blend (alpha=1.0f)
1538  if (!node->GetState().IsEnabled(GL_BLEND)) {
1539  alpha = 1.0f;
1540  }
1541  else {
1542  if (node->GetState().BlendFuncSet() &&
1543  (node->GetState().GetSourceBlendFactor() != GL_SRC_ALPHA ||
1544  node->GetState().GetTargetBlendFactor() != GL_ONE_MINUS_SRC_ALPHA)) {
1545  alpha = 1.0f;
1546  }
1547  }
1548 
1549  return x_GetAlphaGraphicsState(alpha);
1550 }
1551 
1552 
1554 {
1555  string state_name;
1556 
1557  int rounded_alpha = int(alpha*100.0f);
1558  state_name = "A" + NStr::IntToString(rounded_alpha);
1559 
1560  CRef<CPdfElement>& res = (*PageDictionary())["Resources"];
1561  if ( !res ) {
1562  res.Reset(new CPdfDictionary());
1563  }
1564  CRef<CPdfDictionary> _res(dynamic_cast <CPdfDictionary*>(res.GetPointer()));
1565 
1566  CRef<CPdfElement>& extgstate = (*_res)["ExtGState"];
1567  if ( !extgstate ) {
1568  extgstate.Reset(new CPdfDictionary());
1569  }
1570  CRef<CPdfDictionary> _extgstate(dynamic_cast <CPdfDictionary*>(extgstate.GetPointer()));
1571 
1572  CRef<CPdfElement>& gs = (*_extgstate)[state_name];
1573  if (gs.IsNull()) {
1574  TPdfObjectRef graphics_state;
1575  graphics_state.Reset(new CPdfObject(m_ObjIdGenerator->NextId()));
1576  CPdfObject& state = *graphics_state;
1577  state["Type"] = new CPdfName("ExtGState");
1578  state["ca"] = new CPdfNumber(alpha);
1579  state["CA"] = new CPdfNumber(alpha);
1580  m_ObjectWriter->WriteObject(graphics_state);
1581  gs.Reset(new CPdfIndirectObj(graphics_state));
1582  }
1583  return state_name;
1584 }
1585 
#define false
Definition: bool.h:36
CGlModel2D Base class for a model set up for rendering purposes.
Definition: glmodel2d.hpp:64
class CGlPane
Definition: glpane.hpp:62
CGlVboNode A rendering node that holds a vertex buffer object.
Definition: glvbonode.hpp:64
CIdGenerator - generates consecutive integer identifiers.
string GenShaderID()
Get a new (unique) shader ID of form sh##.
const TObjectList & GetFontObjects(void) const
list< TPdfObjectRef > TObjectList
void PushGraphicsState()
gs
Definition: pdf_object.cpp:73
void SetLineDashStyle(int factor, short pattern)
Definition: pdf_object.cpp:127
void PopGraphicsState()
Definition: pdf_object.cpp:78
void Tri(CVect2< float > &p1, CVect2< float > &p2, CVect2< float > &p3)
Definition: pdf_object.cpp:274
void StartTris(EPdfFilter filter, EBitCount bits_per_coord, int range_minx, int range_maxx, int range_miny, int range_maxy)
Definition: pdf_object.cpp:305
void Line(CVect2< float > &p1, CVect2< float > &p2)
Definition: pdf_object.cpp:183
CPdfDictionary & GetDictionary(void)
Definition: pdf_object.cpp:569
void SetLineCapStyle(int cap_style)
Definition: pdf_object.cpp:117
void Text(CRef< CPdfFontHandler > font_handler, EFontFace face, float font_size, CVect2< float > &p, const char *txt)
Definition: pdf_object.cpp:203
void SetClipBox(int x, int y, int width, int height)
Definition: pdf_object.cpp:191
void EndTris()
Definition: pdf_object.cpp:349
void SetFillColor(const CRgbaColor &c)
rg
Definition: pdf_object.cpp:100
void SetGraphicsState(const string &state)
Definition: pdf_object.cpp:83
void SetColor(const CRgbaColor &c)
RG.
Definition: pdf_object.cpp:88
void Poly(vector< CVect2< float > > &verts)
Definition: pdf_object.cpp:281
void SetLineJoinStyle(int join_style)
Definition: pdf_object.cpp:122
void Quad(CVect2< float > &p1, CVect2< float > &p2, CVect2< float > &p3, CVect2< float > &p4)
Definition: pdf_object.cpp:294
void SetLineWidth(float w)
Definition: pdf_object.cpp:112
unique_ptr< CPdfObjectWriter > m_ObjectWriter
Definition: pdf.hpp:184
EContentType
Definition: pdf.hpp:64
@ ePdfLines
Definition: pdf.hpp:64
@ ePdfPoints
Definition: pdf.hpp:64
@ ePdfFlatTris
Definition: pdf.hpp:64
@ ePdfTris
Definition: pdf.hpp:64
@ ePdfQuads
Definition: pdf.hpp:64
void x_PrintFlatTriBuffer(CGlVboNode *node, const TVPRect &vp, CRgbaGradColorTable *color_table=NULL)
For triangles in which vertices are all the same color.
Definition: pdf.cpp:859
CRef< CPdfDictionary > PageDictionary()
Definition: pdf.hpp:97
void AddTooltip(CGlPane &pane, const string &txt, CVect4< float > &rect)
Definition: pdf.cpp:307
virtual void EndAnnot()
Definition: pdf.cpp:187
CRef< CPdfTrailer > m_Trailer
Definition: pdf.hpp:200
vector< TPdfObjectRef > m_PrintInEndDoc
Definition: pdf.hpp:193
virtual void BeginPage(void)
Definition: pdf.cpp:204
void RenderShaderInstance(CGlVboNode *node, const string &shader_id, const TVPRect &vp)
Display an instance of a set of shaded triangles "shader_id", which is returned by AddShadedTris.
Definition: pdf.cpp:1244
CRef< CIdGenerator > m_ObjIdGenerator
Definition: pdf.hpp:185
TPdfObjectRef m_CurrentAnnot
Definition: pdf.hpp:197
void PrintQuadBuffer(CGlVboNode *node, const TVPRect &vp, CRgbaGradColorTable *color_table=NULL)
Quads are only supported as non-shaded (records error if shaded)
Definition: pdf.cpp:1334
virtual void EndPage(void)
Definition: pdf.cpp:208
virtual ~CPdf()
Definition: pdf.cpp:69
TPdfObjectRef m_Fonts
Definition: pdf.hpp:201
void AddJSTooltip(CGlPane &pane, const string &txt, const string &title, CVect4< float > &rect)
Definition: pdf.cpp:384
virtual void BeginDocument(void)
Definition: pdf.cpp:88
void PrintModel(CGlPane &pane, CGlModel2D &model, CRgbaGradColorTable *color_table=NULL)
Print contents of (2D) model.
Definition: pdf.cpp:269
bool m_IsGreyScale
Definition: pdf.hpp:188
virtual void SetOptions(const CPrintOptions &options)
Definition: pdf.cpp:74
CRef< CPdfDictionary > m_PageDictionary
Definition: pdf.hpp:202
unique_ptr< CPageHandler > m_PageHandler
Definition: pdf.hpp:199
TPdfObjectRef m_CurrentContent
Definition: pdf.hpp:196
virtual void SetOutputStream(CNcbiOstream *ostream)
Definition: pdf.cpp:81
void PrintTriBuffer(CGlVboNode *node, const TVPRect &vp, CRgbaGradColorTable *color_table=NULL)
Prints AddShadedTris and RenderShaderInstance to save an instance of shaded triangles to the pdf.
Definition: pdf.cpp:1304
virtual string x_GetAlphaGraphicsState(CGlVboNode *node)
Definition: pdf.cpp:1496
string AddShadedTris(CGlVboNode *node, const TVPRect &vp, CRgbaGradColorTable *color_table, CPdfObject::EBitCount bit_count=CPdfObject::e16Bit)
Add a triangle buffer to the pdf (but do not display it) Returns the ID of the triangle shader object...
Definition: pdf.cpp:1109
bool x_GetColors(CGlVboNode *node, vector< CRgbaColor > &colors, CRgbaGradColorTable *color_table)
Return true if node has color buffer and update array with those colors.
Definition: pdf.cpp:480
virtual void EndReference()
Definition: pdf.cpp:199
CRef< CPdfFontHandler > m_FontHandler
Definition: pdf.hpp:203
virtual void EndContent()
Definition: pdf.cpp:174
virtual void EndDocument(void)
Definition: pdf.cpp:1471
void PrintLineBuffer(CGlVboNode *node, const TVPRect &vp, CRgbaGradColorTable *color_table=NULL)
Prints the lines in buffer "node" to the pdf using current graphics state, modelview and projection m...
Definition: pdf.cpp:618
TPdfObjectRef m_CurrentReference
Definition: pdf.hpp:198
void PrintText(CRef< CPdfFontHandler > font_handler, EFontFace face, float font_size, CVect2< float > &p, const char *txt, const CRgbaColor &c)
Write a string of text at the specified position (in screen coords).
Definition: pdf.cpp:219
virtual TPdfObjectRef BeginContent(EContentType t)
Definition: pdf.cpp:167
TPdfObjectRef m_Catalog
Definition: pdf.hpp:195
void PrintPointBuffer(CGlVboNode *node, const TVPRect &vp, CRgbaGradColorTable *color_table=NULL)
Prints the points in buffer "node" to the pdf using current graphics state, modelview and projection ...
Definition: pdf.cpp:518
virtual TPdfObjectRef BeginReference()
Definition: pdf.cpp:192
CPdf()
Definition: pdf.cpp:55
virtual TPdfObjectRef BeginAnnot()
Definition: pdf.cpp:180
void WriteObject(TPdfObjectRef &obj)
Definition: pdf.cpp:162
void PrintBuffer(CGlVboNode *node, const TVPRect &viewport, CRgbaGradColorTable *color_table=NULL)
Checks the buffer type and prints accodingly tris, points or lines.
Definition: pdf.cpp:446
unsigned int GetNumPages(void) const
TPdfUnit GetUserUnit() const
bool GetGouraudShadingDisabled() const
const string & GetTitle(void) const
bool GetPrintOutput(void) const
class CRgbaColor provides a simple abstraction for managing colors.
Definition: rgba_color.hpp:58
CRgbaGradColorTable Provides a storage for colors (to eliminate color creation overhead) and Function...
Simple helper class to take a bunch of triangles and extract from that set, if possible,...
size_t AddTri(CVect2< float > &v1p, CVect2< float > &v2p, CVect2< float > &v3p)
std::vector< CVect2< float > > GetPerimiter() const
returns empty vec if no perimeter can be found.
unsigned int m_PageCount
virtual void SetOutputStream(CNcbiOstream *ostream)
unique_ptr< CPageBuffers > m_PageBuffers
virtual CPrintOptions & GetOptions()
CPrintOptions m_Options
CNcbiOstream * m_Strm
virtual void SetOptions(const CPrintOptions &options)
static const Colors colors
Definition: cn3d_colors.cpp:50
#define ITERATE(Type, Var, Cont)
ITERATE macro to sequence through container elements.
Definition: ncbimisc.hpp:815
#define NULL
Definition: ncbistd.hpp:225
#define LOG_POST(message)
This macro is deprecated and it's strongly recomended to move in all projects (except tests) to macro...
Definition: ncbidiag.hpp:226
void Warning(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1191
void Identity()
Definition: matrix3.hpp:313
T & Z()
Definition: vect4.hpp:115
void Transpose()
Definition: matrix4.hpp:347
const CVect2< U > & v2
Definition: globals.hpp:440
T & X()
Definition: vect2.hpp:107
T & X()
Definition: vect4.hpp:111
const T * GetData() const
Definition: matrix4.hpp:103
T & Y()
Definition: vect4.hpp:113
T & W()
Definition: vect4.hpp:117
T & Y()
Definition: vect2.hpp:109
GLenum GetShadeModel() const
Definition: glstate.hpp:240
int gluProjectX(GLdouble objx, GLdouble objy, GLdouble objz, const GLdouble modelMatrix[16], const GLdouble projMatrix[16], const GLint viewport[4], GLdouble *winx, GLdouble *winy, GLdouble *winz)
Definition: glutils.hpp:223
GLenum GetPolygonMode() const
Definition: glstate.hpp:276
void Get2DVertexBuffer(std::vector< CVect2< float > > &verts) const
Definition: glvbonode.cpp:159
bool GetDefaultColor(CRgbaColor &default_color, bool is_greyscale) const
Return default color from node in default_color.
Definition: glvbonode.cpp:207
bool LineWidthSet() const
Definition: glstate.hpp:231
T Height() const
Definition: glrect.hpp:90
CRgbaColor GetTexCoordColor(float f) const
Get color given a texture coord.
CVect2< TModelUnit > GetScale() const
Definition: glpane.cpp:128
virtual CMatrix4< float > GetProjectionMatrix() const =0
void GetColorBuffer(vector< CRgbaColor > &data) const
Definition: glvbonode.cpp:110
vector< CGlVboNode * > & GetTempNodes()
Definition: glmodel2d.hpp:124
GLfloat GetLineWidth() const
Definition: glstate.hpp:230
bool BlendFuncSet() const
Definition: glstate.hpp:301
T Bottom() const
Definition: glrect.hpp:82
virtual CMatrix4< float > GetModelViewMatrix() const =0
T Width() const
Definition: glrect.hpp:86
virtual void MatrixMode(GLenum mode)=0
vector< CGlVboNode * > & GetNodes()
Definition: glmodel2d.hpp:106
ESecondaryFormat
Definition: ivbogeom.hpp:66
void GetColorBufferUC(vector< CVect4< unsigned char > > &data) const
Definition: glvbonode.cpp:124
CMatrix4< float > GetTransformedPosition(size_t idx)
return the position with rotation and pixel offset baked in
IRender & GetGl()
convenience function for getting current render manager
TVPRect & GetViewport(void)
Definition: glpane.hpp:332
vector< CMatrix4< float > > & GetPositions()
return the current set of transformations for thisnode
GLfloat GetPointSize() const
Definition: glstate.hpp:235
CGlState & GetState()
bool LineCapStyleSet() const
Definition: glstate.hpp:345
bool IsEnabled(GLenum glstate) const
Return true if option is in m_Enabled list for this state.
Definition: glstate.cpp:371
GLenum GetSourceBlendFactor() const
Definition: glstate.hpp:299
EPdfShadeStyle GetPdfShadeStyle() const
Definition: glstate.hpp:353
virtual void PopMatrix()=0
T Left() const
Definition: glrect.hpp:81
void GetLineStipple(GLint &factor, GLushort &pattern) const
Definition: glstate.cpp:542
void GetTexCoordBuffer1D(vector< float > &data) const
Definition: glvbonode.cpp:138
bool PointSizeSet() const
Definition: glstate.hpp:236
bool LineJoinStyleSet() const
Definition: glstate.hpp:340
static bool CheckGlError()
Check if there are any OpenGL errors.
Definition: glutils.cpp:166
ELineCapStyle GetLineCapStyle() const
Definition: glstate.hpp:344
size_t GetVertexCount() const
Definition: glvbonode.hpp:84
CRgbaColor GetColor() const
Definition: glstate.hpp:259
GLenum GetTargetBlendFactor() const
Definition: glstate.hpp:300
EFontFace
Set of pre-defined fonts for which we know we have valid font files.
virtual void LoadMatrixf(const GLfloat *m)=0
IVboGeom::ESecondaryFormat GetSecondaryFormat() const
Definition: glvbonode.cpp:68
ELineJoinStyle GetLineJoinStyle() const
Definition: glstate.hpp:339
bool ColorSet() const
Definition: glstate.hpp:260
GLenum GetDrawMode() const
Return the current drawing mode (e.g.
Definition: glvbonode.hpp:90
virtual void PushMatrix()=0
bool LineStippleSet() const
Definition: glstate.hpp:282
@ eMiteredJoin
Definition: glstate.hpp:77
@ kSecondaryFormatColorUChar
Definition: ivbogeom.hpp:69
@ kSecondaryFormatColorFloat
Definition: ivbogeom.hpp:68
@ kSecondaryFormatTexture1D
Definition: ivbogeom.hpp:71
@ eRenderPDF
Definition: glstate.hpp:61
@ eButtCap
Definition: glstate.hpp:78
unsigned int NextId(void)
static float ColorDistance(const CRgbaColor &c1, const CRgbaColor &c2)
returns the distance in the RGB color cube between the two colors, scaled to a range [0,...
float GetAlpha(void) const
Definition: rgba_color.hpp:339
TObjectType * GetPointer(void) THROWS_NONE
Get pointer,.
Definition: ncbiobj.hpp:998
void Reset(void)
Reset reference object.
Definition: ncbiobj.hpp:773
bool IsNull(void) const THROWS_NONE
Check if pointer is null – same effect as Empty().
Definition: ncbiobj.hpp:735
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
IO_PREFIX::ostream CNcbiOstream
Portable alias for ostream.
Definition: ncbistre.hpp:149
#define CT_POS_TYPE
Definition: ncbistre.hpp:730
static string IntToString(int value, TNumToStringFlags flags=0, int base=10)
Convert int to string.
Definition: ncbistr.hpp:5083
static enable_if< is_arithmetic< TNumeric >::value||is_convertible< TNumeric, Int8 >::value, string >::type NumericToString(TNumeric value, TNumToStringFlags flags=0, int base=10)
Convert numeric value to string.
Definition: ncbistr.hpp:673
unsigned int
A callback function used to compare two keys in a database.
Definition: types.hpp:1210
int i
static MDB_envinfo info
Definition: mdb_load.c:37
const struct ncbi::grid::netcache::search::fields::SIZE size
Defines: CTimeFormat - storage class for time format.
T max(T x_, T y_)
T min(T x_, T y_)
Int4 delta(size_t dimension_, const Int4 *score_)
CNcbiOstream & pdfeol(CNcbiOstream &strm)
int offset
Definition: replacements.h:160
static const char * catalog
Definition: stats.c:19
#define N
Definition: crc32.c:57
Modified on Sat Dec 09 04:45:57 2023 by modify_doxy.py rev. 669887