(DOORS/DXL) Экспорт в pdf или печать с заданным шаблоном

Нас интересует экспорт/печать одного представления из формального модуля. Как мы хотим, это может быть официальный документ, мы хотим добавить наш корпоративный шаблон, который имеет свою собственную главную страницу, и свои собственные верхние и нижние страницы с заданным текстовым шрифтом, размером и цветом, и вставленными изображениями (символ компании, клиентские ).

Насколько я понимаю, DOORS позволяет печатать с добавлением верхнего и нижнего колонтитула, но без изображений. С другой стороны, можно экспортировать в Word, добавив шаблон, но я не могу сделать это напрямую в формат pdf или на принтер.

Для нас важно, чтобы корпоративный документ можно было сделать напрямую в Doors без Word или какого-либо другого редактора, чтобы документ DOORS мог быть напрямую выпущен клиентам без внешних изменений.

Возможно ли это сделать с помощью DXL-скрипта? Или есть еще какой вариант сделать?

Я полагаю, что многим пользователям приходится распечатывать (или создавать pdf-файлы) корпоративные изображения своей компании, и я думаю, что это может быть очень полезно.

Спасибо заранее за вашу помощь.

С наилучшими пожеланиями,

JM


person Jose Manuel Moreno Ramos    schedule 07.11.2012    source источник


Ответы (3)


Возможно ли это сделать с помощью DXL-скрипта?

ИМХО это. Я уже написал сценарий DXL для экспорта модулей DOORS в файл LaTeX, который можно скомпилировать в PDF. Вы найдете его в конце этого ответа. Не стесняйтесь адаптировать его к вашим потребностям. Прежде чем вы сможете запустить его, вам необходимо загрузить исходный код "Smart Folder Browser" и сохраните его помимо моего сценария как «smartFolderBrowser.inc». Расширение Die inc указывает, что это включаемый файл, а не отдельная программа DXl.

Конечно, каждому пользователю, который хочет использовать такой подход, необходим установленный дистрибутив TeX, такой как MiKTeX. Сценарий DXL может запустить сборку PDF после экспорта с помощью встроенной функции DXL void system(string command).

С помощью собственного скрипта экспорта DXL LaTeX вы сможете полностью контролировать макет итогового PDF-файла. Но помните о правилах компоновки, которые применяет TeX. Может оказаться очень сложной задачей настроить (заставить) TeX отображать документ в том виде, в котором он был создан с помощью M$ Word.

// DOORS LaTeX Export
/**
Copyright (c) 2012-2013 Kai K.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
pragma encoding, "UTF-8"


#include "smartFolderBrowser.inc"

DBE dbeOutputFolder = null;
DBE dbeOptions = null;
bool withTitles = true
Buffer tempBuf = create;

void escapeSpecialLaTeXCharacters(Buffer& str)
{
   Buffer temp = create;

   int i = 0;
   for(i = 0; i < length(str); ++i) 
   {
      char c = str[i]; 
      if( '\\' == c ) 
      {
         temp += "\\textbackslash{}";
      }
      else if( '{' == c )
      {
         temp += "\\"
         temp += c
      }
      else if( '}' == c )
      {
         temp += "\\"
         temp += c
      }
      else if( '_' == c )
      {
         temp += "\\"
         temp += c
      }
      else if( '^' == c )
      {
         temp += "\\textasciicircum{}";
      }
      else if( '#' == c )
      {
         temp += "\\"
         temp += c
      }
      else if( '&' == c )
      {
         temp += "\\"
         temp += c
      }
      else if( '$' == c )
      {
         temp += "\\"
         temp += c
      }
      else if( '%' == c )
      {
         temp += "\\"
         temp += c
      }
      else if( '~' == c )
      {
         temp += "\\textasciitilde{}";
      }
      else
      {
         temp += c;
      }
   }

   str = tempStringOf(temp);
   delete temp;
}

string makeLabel(Buffer& str)
{
   setempty(tempBuf);

   int i = 0;
   for(i = 0; i < length(str); ++i) 
   {
      char c = str[i]; 
      if( ' ' != c && '-' != c && '\n' != c && '\\' != c && '_' != c && '#' != c) 
      {
         tempBuf += c;
      }
   }
   return stringOf(tempBuf);
}

string makeCaption(Buffer& str)
{
   setempty(tempBuf);

   int i = 0;
   for(i = 0; i < length(str); ++i) 
   {
      char c = str[i]; 
      if( '\n' != c && '\\' != c) 
      {
         tempBuf += c;
      }
   }
   escapeSpecialLaTeXCharacters(tempBuf);
   return stringOf(tempBuf);
}

void rtf2latex(Buffer& buf)
{
   Buffer txt = create;
   Buffer temp = create;
   RichTextParagraph rp = null;
   bool envopened = false;
   for rp in tempStringOf(buf) do {
      if(rp.indentLevel > 0)
      {
         real hspace = realOf(rp.indentLevel)/20.0;
         temp += "{\\hangindent" hspace "pt\\hangafter0"
      }
      if(rp.isBullet)
      {
         temp += "\\textbullet ";
      }      
      RichText rt = null      
      for rt in rp do {
         txt = rt.text;         
            escapeSpecialLaTeXCharacters(txt);

         if (rt.italic)
         {
            temp += "\\textit{";
            temp += txt;
            temp += "}";
         }
         else if (rt.bold)
         {
            temp += "\\textbf{";
            temp += txt;
            temp += "}";
         }
         else if (rt.strikethru)
         {
            temp += "\\sout{";
            temp += txt;
            temp += "}";
         }
         else if (rt.subscript)
         {
            temp += "\\textsubscript{";
            temp += txt;
            temp += "}";
         }
         else if (rt.superscript)
         {
            temp += "\\textsuperscript{";
            temp += txt;
            temp += "}";
         }
         else if (rt.underline)
         {
            temp += "\\underline{";
            temp += txt;
            temp += "}";
         }         
         else
         {
            temp += txt;
         }
         if (rt.newline && !rt.last)
         {
            temp += "\\par\n";
         }
         else if(rt.last)
         {
            temp += "\n";
         }
      }
      if(rp.indentLevel > 0)
      {
         temp += "}"
      }
   }
   buf = tempStringOf(temp);
   delete temp;
   delete txt;  
}

void getObjectText(Object obj, Buffer& buf)
{  
   if(!null(obj))
   {
      buf = richTextFragment(richText(obj."Object Text"));
      rtf2latex(buf);
   }
   else
   {
      buf = "null";
      print("ERROR: obj null!: "dxlHere() "\n");
   }   
}

int nextCell(Stream& oss, int curCol, int numCols )
{
   if( curCol == numCols )
   {
      oss << "\\\\\n\\hline\n"
   }
   else
   {
      oss << "\t&\t"
   }

   return( curCol % numCols + 1 )
}

void writePreamble(Stream& oss)
{
   oss << "\\documentclass[a4paper]{book}\n";
   oss << "%---- packages ----\n";
   oss << "\\usepackage{ulem}\n";
   oss << "\\usepackage{color}\n";
   oss << "\\usepackage{graphicx}\n";
   oss << "\\usepackage{supertabular}\n";
   oss << "\\usepackage{hyperref}\n";
   oss << "\\usepackage{makeidx}\n";
   oss << "\\usepackage{xltxtra}\n";
   oss << "\\makeindex\n";
   oss << "%\n---- settings ----\n";
   oss << "\\setcounter{secnumdepth}{6}\n";
   oss << "\\setlength{\\parindent}{0}\n";
   oss << "\\setlength{\\parskip}{2x}\n";
   oss << "%\n---- customizations ----\n";
   oss << "\\makeatletter\n";
   oss << "\\def\\maxwidth{%\n";
   oss << "\\ifdim\\Gin@nat@width>\\linewidth\n";
   oss << "\\linewidth\n";
   oss << "\\else\n";
   oss << "\\Gin@nat@width\n";
   oss << "\\fi\n";
   oss << "}\n";
   oss << "\\makeatother\n";
   oss << "\n%---- document ----\n";
   oss << "\\begin{document}\n";
   oss << "\\tableofcontents\n";
   oss << "\\listoffigures\n";
   oss << "\\listoftables\n";
}

void writeTableHeader(Stream& oss, Object tableobj, int& numCols )
{
   //print("Enter: writeTableHeader\n");
   numCols = 0
   int numRows = 0;
   int tableWidth = 0;

   Object cellobj = null;
   Object rowobj = null;
   // first count the columns and rows
   for rowobj in table( tableobj ) do {
      if( !isDeleted( rowobj ) )
      {
         if(numCols == 0)
         {
            for cellobj in rowobj do {
               if( !isDeleted( cellobj ))
               {
                  tableWidth += getCellWidth(cellobj);
                  numCols++;              
               }
            }
         }
         numRows++;
      }
   }

   // extract the header row
   int colCount = numCols;
   int col = 0;   
   Object headrow[colCount];
   for rowobj in table( tableobj ) do {
      if( !isDeleted( rowobj ) )
      {
         if(col == 0)
         {
            for cellobj in rowobj do {
               if( !isDeleted( cellobj ))
               {
                  headrow[col] = cellobj;
                  col++;
               }
            }
         }
         else
         {            
            break;
         }
      }
   }

   // export the table head
   oss << "\\begin{centering}\n";

   Buffer buf = create;
   oss << "\\tablefirsthead{";   
   for(col=0; col<colCount; ++col) {
      getObjectText(headrow[col], buf);
      oss << tempStringOf(buf);
      if(col+1<colCount)
         oss << "&";
   }
   oss << "\\\\}\n";

   oss << "\\tablehead{";
   for(col=0; col<colCount; ++col) {   
      getObjectText(headrow[col], buf);
      oss << tempStringOf(buf);
      if(col+1<colCount)
         oss << "&";         
   }
   oss << "\\\\}\n";
   oss << "\\tabletail{\\hline}\n";   

   oss << "\\begin{supertabular}{|";
   for(col=0; col<colCount; ++col) {
      cellobj = headrow[col];
      int w = getCellWidth(cellobj);
      real rw = w;
      real tw = tableWidth;
      w = intOf(rw/tw*100.0);
      rw = realOf(w)/100.0;
      oss << "p{" rw "\\textwidth}|";
   }  
   oss << "}\n\\hline\n"

   delete buf;

   //print("Leave: writeTableHeader\n");
}

void writeTableFooter(Stream& oss, Buffer& objtext)
{
   oss << "\\\\\n\\hline\n"
   oss << "\\end{supertabular}\n\\label{tab:";
   oss << makeLabel( objtext );
   oss << "}\n\\bottomcaption{"
   oss << makeCaption( objtext );
   oss << "}\n\\end{centering}\n"
}

void writeobjheading(Stream& oss, Buffer &objNum, Buffer &text, int level )
{
   if(1 == level)
   {
      oss << "\\chapter{";
   }
   else if(2 == level)
   {
      oss << "\\section{";        
   }
   else if(3 == level)
   {
      oss << "\\subsection{";        
   }
   else if(4 == level)
   {
      oss << "\\subsubsection{";        
   }
   else if(5 == level)
   {
      oss << "\\paragraph{";        
   }
   else
   {
      oss << "\\subparagraph{";        
   }

   oss << tempStringOf(text);
   oss << "}\n\\label{sec:";
   oss << makeLabel(text);
   oss << makeLabel(objNum);
   oss << "}\n";
}

void writeFigureHeadAndExport(Stream& oss, Object img, string outputDir)
{
   Module mod = module(img);   
   string n = mod."Prefix"img."Absolute Number"".png";
   string s = exportPicture(img, outputDir "\\" n, formatPNG);   
   oss << "\\begin{figure}[ht]\n";
   oss << "\\centering\n";
   oss << "\\includegraphics[width=\\textwidth]{"n"}\n";
}

void writeFigureFooter(Stream& oss, Buffer& objtext)
{
   oss << "\\label{fig:";
   oss << makeLabel( objtext );
   oss << "}\n\\caption{"
   oss << makeCaption( objtext );
   oss << "}\n\\end{figure}\n";
}

void writeRequirement(Stream& oss, Module& doorsModule, Object obj, Buffer& puid, Buffer& objtext)
{
   oss << "\\textbf{";
   oss << tempStringOf( puid );
   oss << "}\\\\\n" //"PUID style"
   oss << "\\label{req:";
   oss << makeLabel(puid)
   oss << "}\n";
   oss << "\\index{";
   oss << tempStringOf( puid );
   oss << "}\n";
   oss << "\\color{blue}\n"
   oss << tempStringOf( objtext )
   oss << "\n"//"requirement style"

   oss << "\\begin{tabbing}\n"
   Column col = null;
   Buffer var_name = create;
   int c=0;   
   for col in doorsModule do {      
      var_name = title( col )
      escapeSpecialLaTeXCharacters(var_name);

      if( ! main( col ) && search( regexp "(P|p)(U|u)(I|i)(D|d)", var_name, 0 ) == false )
      {         
         oss << "\\textbf{";
         oss << var_name;
         if(c == 0)
            oss <<  "}: \\hspace{2.0cm} \\= "
         else
            oss <<  "}: \\> "

         var_name = text( col, obj );
         escapeSpecialLaTeXCharacters(var_name);
         oss << var_name;
         oss << "\\\\\n";// "attribute valueBuf" )
         c++;
      }               
   }
   oss << "\\end{tabbing}\n"
   oss << "\\color{black}\n"
   delete var_name;
}

void timeString( int timeInSeconds, Buffer &t )
{
   t = ""

   int hours, minutes, seconds

   hours    =   timeInSeconds / 3600
   minutes  = ( timeInSeconds - hours * 3600 ) / 60
   seconds  = ( timeInSeconds - hours * 3600 - minutes * 60 )

   if( hours < 10 ) t = "0"
   t += ( hours "")
   t += ":"
   if( minutes < 10 ) t += "0"
   t += ( minutes "")
   t += ":"
   if( seconds < 10 ) t += "0"
   t += ( seconds "")
}

void doExport(DB db)
{
   int startTime = intOf( today )

   int progressLimit = 0;
   Object obj = null;
   for obj in current Module do {
      progressLimit++;
   }

   progressStart(db, "Exporting Module as LaTeX", "Object 0/"progressLimit"", progressLimit);
   string outputDir = get(dbeOutputFolder);
   string outputFileName = name(current Module);
   string outputFileExt = ".tex";
   string outputFilePath = outputDir "\\" outputFileName outputFileExt;

   Stream mainFile = write(outputFilePath);
   Stream oss = mainFile;

   writePreamble(oss);

   int progress = 1;
   int curCol = 1;
   int numCols = 0;
   bool lastObjWasFigure = false;
   bool lastObjWasTable = false;

   Buffer objheading = create;
   Buffer objtext = create;
   Buffer objNum = create;
   Buffer puid = create;

   int lev = 0
   int puidLevel = 0
   Regexp excel = regexp "objclass Excel.Sheet"
   Module doorsModule = current Module;
   int subfileCount = 1;
   for obj in current Module do {
      if(progressCancelled())
      {
         if( confirm(db, "Do you really want to abort the export?") )
         {
            break;
         }
      }

      progressMessage("Object "progress"/"progressLimit"");

      getObjectText(obj, objtext)

      // ------------------- Handle Tables ------------------------------------
      if( cell( obj ))
      {
         if( !lastObjWasTable )
         {
            writeTableHeader(oss, obj, numCols);            
            curCol           = 1;
            lastObjWasTable  = true;
         }
         else
         {
            curCol = nextCell(oss, curCol, numCols );
         }
         oss << tempStringOf( objtext ); // "Standard"
         progressStep(progress);
         progress++;
         continue
      }

      // ------------------- After Table write Table Title -----------------
      if( lastObjWasTable )
      {
         writeTableFooter(oss, objtext);
      }
      // ------------------- After Figure write Figure Title ---------------
      if( lastObjWasFigure )
      {
         writeFigureFooter(oss, objtext)
      }

      objNum  = number( obj )
      objheading = obj."Object Heading"
      escapeSpecialLaTeXCharacters(objheading)

      // ------------------- Handle End of Requirement ------------------------
      lev = level( obj )
      if( lev <= puidLevel )
      {
         //oss << "End Requirement\n\n"
         puidLevel = 0
      }

      if( withTitles && ( lastObjWasTable || lastObjWasFigure ))
      {
         lastObjWasTable  = false
         lastObjWasFigure = false
         continue
      }

      // ------------------- Handle objheading with hierarchy --------------------
      if( length( objheading ) > 0 )
      {
         writeobjheading(oss, objNum, objheading, lev )
      }

      if( length( objtext ) > 0 )
      {
         // remember, if Title has to be written after this object
         if( containsOle( obj."Object Text"))
         {
            if( excel objtext ) {
               lastObjWasTable = true
            }
            else
            {
               lastObjWasFigure = true;
               writeFigureHeadAndExport(oss, obj, outputDir);
            }
         }
         // ------------------- Handle Requirements objects Text -----------
         puid = obj."IE PUID"
         escapeSpecialLaTeXCharacters(puid)
         if( length( puid ) > 0 )
         {
            puidLevel = lev
            writeRequirement(oss, doorsModule, obj, puid, objtext);
         }
         // ------------------- No PUID means normal text Object -----------
         else
         {
            oss << tempStringOf( objtext );
            oss << "\n";// "Standard"
         }
      }


      progressStep(progress);
      progress++;      
   }

   oss << "\\printindex\n"
   oss << "\\end{document}\n";

   close(oss);
   progressStop();

   // ---------------------- show the result ----------------------------------
   int endTime = intOf( today ) - startTime

   Buffer totalTime = create;
   timeString( endTime, totalTime );

   infoBox( "Export successfully finished after " totalTime "\nThe result is located in\n" outputFilePath);

   delete objheading;
   delete objtext;
   delete objNum;
   delete puid;
   delete tempBuf;
}

DB db = create("LaTeX Export");
dbeOutputFolder = smartFolderBrowser(db, "Output Folder:", false);

ok(db, doExport);
show(db);
person Kai K.    schedule 08.11.2012
comment
Привет. Спасибо за ваш ответ. Я начну узнавать кое-что о MikTeX. Это новое для меня, поэтому я собираюсь проверить его, чтобы увидеть, смогу ли я его использовать. Поскольку я не программист dxl, я не уверен в этом. - person Jose Manuel Moreno Ramos; 15.11.2012
comment
Я загрузил MikTeX. Насколько я вас понял, мне нужен ваш DXL-скрипт для экспорта LaTeX-файла, пересматриваю все вопросы с тегом dxl, но не нашел. Что касается подготовки пользовательского DXL для получения персонализированной обложки и верхних и нижних колонтитулов с изображениями, не могли бы вы дать мне более подробную информацию? Я новичок и не особо разбираюсь в программировании на dxl. Спасибо!! - person Jose Manuel Moreno Ramos; 15.11.2012
comment
@ Хосе: Конечно, ты его не нашел. Я никогда не публиковал код. Я посмотрю, смогу ли я загрузить код куда-нибудь и поделиться ссылкой здесь. Но это может занять некоторое время... До тех пор я рекомендую вам начать читать справочник по DXL, предоставленный DOORS. - person Kai K.; 15.11.2012
comment
Благодарю вас! Однако, когда я пытаюсь запустить его, отображается ошибка: italic bold -E- DXL: <Line:27> could not open include file (utils/smartFolderBrowser.inc) (No such file or directory) - person Jose Manuel Moreno Ramos; 31.01.2013
comment
@Jose: я добавил примечание о включаемом файле и исправил две ошибки сценария. Я с нетерпением жду, когда мой ответ будет принят. - person Kai K.; 31.01.2013
comment
Я сохранил smartFolderBrowser.inc, после чего запускаю dxl. Отображается следующее сообщение -R-E- DXL: <Line:508> null dialog element (DBE) parameter was passed into argument position 1 . В указанной папке нет файла, сохраненного сыном, я не знаю, что мне теперь делать. Пожалуйста, простите меня за то, что доставил вам некоторые проблемы с этим. - person Jose Manuel Moreno Ramos; 04.02.2013
comment
@Jose: Теперь я вспомнил, почему рекомендуется тестировать код, прежде чем делиться им. Я удалил ошибочные строки и отключил некоторые отладочные данные. Но я рекомендую вам научиться отлаживать и писать сценарии DXL. Эти навыки необходимы для адаптации сценария к вашим потребностям. Возможно, вам придется нанять консультантов DOORS/DXL, чтобы помочь вам. - person Kai K.; 05.02.2013
comment
Опоздание на вечеринку: я получаю сообщение об ошибке: неизвестный объект IE PUID в строке 611, `puid = obj.IE PUID. ДВЕРИ 9.3 - person Carl Witthoft; 25.08.2016
comment
@CarlWitthoft: измените доступ к свойству по своему усмотрению. Мы использовали свойство IE PUID в качестве удобочитаемого идентификатора требования. Измените его на obj.‹your_id_prop› - person Kai K.; 31.08.2016
comment
@КайК. Спасибо - на самом деле это то, что я сделал после некоторого совета здесь: заголовок stackoverflow.com/questions/39206498/ - person Carl Witthoft; 31.08.2016
comment
Привет, @JoseManuelMorenoRamos, после 8 лет и 5 голосов ты можешь принять мой ответ ;-) - person Kai K.; 10.03.2020

Какую версию DOORS вы используете?

В DOORS 9.3+ IBM включила функции RPE в DOORS, что дает вам больше возможностей для экспорта, включая экспорт в PDF. Однако только в DOORS 9.4.0.1 (текущий выпуск) можно было создать собственный шаблон в IBM Rational Publishing Engine (RPE) и использовать этот шаблон непосредственно в DOORS.

Таким образом, ответ заключается в том, что вам потребуется одна лицензия на RPE Full от IBM, чтобы вы могли создать нужный шаблон, после чего любой, кто использует DOORS в вашей компании, сможет экспортировать документы DOORS, используя этот шаблон, без дополнительных лицензий. Вы также должны быть на DOORS 9.4.0.1.

Я также видел, как некоторые компании экспортируют в Word свои верхние и нижние колонтитулы, а затем используют Adobe Acrobat для добавления на свои титульные страницы логотипов и тому подобного. Однако это означает, что только люди с полной лицензией на Acrobat могут правильно создавать документы.

RPE, безусловно, ваш лучший выбор.

Удачи.

person Steve Valliere    schedule 07.11.2012
comment
Насколько я помню, у нас есть Doors 9.3... так что я предполагаю, что не могу решить эту проблему... Я попытаюсь подтвердить это Спасибо!! - person Jose Manuel Moreno Ramos; 07.11.2012
comment
Я уверен, что это как-то можно сделать с DXL. Но к моменту написания скрипта можно было обновиться до 9.4.0.1. Это не стоило бы усилий. - person Steve Valliere; 07.11.2012
comment
Я подтвердил, что мы установили версию 9.3. Я не уверен, что мы собираемся обновляться до 9.4. Как вы говорите, наш путь - экспортировать в Word, а потом использовать Adobe Acrobat. У нас есть все их лицензии. Однако я хотел избежать этих промежуточных шагов, чтобы кто-то не мог изменить отчет. Поэтому я подумал, что лучше всего будет печатать напрямую с нашим собственным форматом обложки и страницы. В любом случае спасибо за вашу помощь. Мы должны продолжать использовать Word в качестве промежуточного шага для печати отчета Doors, хотя его можно легко изменить. - person Jose Manuel Moreno Ramos; 08.11.2012

"Возможно ли это сделать с помощью DXL-скрипта? Или есть какой-то другой вариант?"

Поскольку я прочитал ваш запрос относительно процесса печати данных DXL в PDF, мы представляем сторонний инструмент, который поможет вам переместить или распечатать сохраненные файлы домино .DXL в формат PDF, не внося в них какие-либо существующие данные.

Программа преобразования файлов DXL в PDF - лучшее решение, помогающее пользователям конвертировать DXL в формат PDF без каких-либо ошибок. Программное обеспечение, имеющее возможность конвертировать файлы или папки DXL одним щелчком мыши.

person Abhay Malhotra    schedule 02.01.2018