Вывести текст вертикально перевернутый при изменении PDF
У меня была проблема с использованием podofo для изменения pdf документа, если у вас было время, пожалуйста, помогите мне решить ее!
Я нашел источник podofo на http://podofo.sourceforge.net/download.html, скомпилировал его на windows 7 x86 и обнаружил, что функция podofo очень мощная.
Но когда я что-то изменяю в примере "helloworld.cpp", нужно лишь немного изменить код, чтобы изменить документ PDF и сохранить его под другим именем!
Когда я передал локальный файл PDF-документа (локальный PDF-документ сохранен из документа Word, использующего интерфейс windows COM на Office Word 2007) в функцию, новый файл выводится успешно, но выводимый текст переворачивается по вертикали, а выводимый текст Y pos вертикально перевернут.
(некоторые ребята говорили, что в такой ситуации вам приходится иметь дело с тем фактом, что существующий контент, возможно, изменил состояние графики, например, изменил текущую матрицу преобразования, возможно, он прав, но я не могу найти функции для изменения графики состояние и изменение текущей матрицы преобразования)
Это снимок экрана, я не знаю, почему текст выводится вертикально:
Странно, что это хорошо работает, когда я передал документ "output.pdf", созданный на примере "helloworld" .
если у вас было время, пожалуйста, помогите мне решить это, большое спасибо!
Мой измененный код выглядит так:
#define MEMDOCUMENT 1 // macro switch
void HelloWorld( const char* pszFilename )
{
/*
* PdfStreamedDocument is the class that can actually write a PDF file.
* PdfStreamedDocument is much faster than PdfDocument, but it is only
* suitable for creating/drawing PDF files and cannot modify existing
* PDF documents.
*
* The document is written directly to pszFilename while being created.
*/
#if MEMDOCUMENT
PdfMemDocument document( pszFilename ); //open local pdf documet
#else
PdfStreamedDocument document( pszFilename ); //create a new pdf documet
#endif
/*
* PdfPainter is the class which is able to draw text and graphics
* directly on a PdfPage object.
*/
PdfPainter painter;
/*
* This pointer will hold the page object later.
* PdfSimpleWriter can write several PdfPage's to a PDF file.
*/
PdfPage* pPage;
/*
* A PdfFont object is required to draw text on a PdfPage using a PdfPainter.
* PoDoFo will find the font using fontconfig on your system and embedd truetype
* fonts automatically in the PDF file.
*/
PdfFont* pFont;
try {
/*
* The PdfDocument object can be used to create new PdfPage objects.
* The PdfPage object is owned by the PdfDocument will also be deleted automatically
* by the PdfDocument object.
*
* You have to pass only one argument, i.e. the page size of the page to create.
* There are predefined enums for some common page sizes.
*/
#if MEMDOCUMENT
pPage = document.GetPage(0); //get the first page and modify it
#else
pPage = document.CreatePage( PdfPage::CreateStandardPageSize( ePdfPageSize_A4 ) );
#endif
/*
* If the page cannot be created because of an error (e.g. ePdfError_OutOfMemory )
* a NULL pointer is returned.
* We check for a NULL pointer here and throw an exception using the RAISE_ERROR macro.
* The raise error macro initializes a PdfError object with a given error code and
* the location in the file in which the error ocurred and throws it as an exception.
*/
if( !pPage )
{
PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
}
/*
* Set the page as drawing target for the PdfPainter.
* Before the painter can draw, a page has to be set first.
*/
painter.SetPage( pPage );
/*
* Create a PdfFont object using the font "Arial".
* The font is found on the system using fontconfig and embedded into the
* PDF file. If Arial is not available, a default font will be used.
*
* The created PdfFont will be deleted by the PdfDocument.
*/
pFont = document.CreateFont( "Arial" );
/*
* If the PdfFont object cannot be allocated return an error.
*/
if( !pFont )
{
PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
}
/*
* Set the font size
*/
pFont->SetFontSize( 18.0 );
/*
* Set the font as default font for drawing.
* A font has to be set before you can draw text on
* a PdfPainter.
*/
painter.SetFont( pFont );
/*
* You could set a different color than black to draw
* the text.
*
* SAFE_OP( painter.SetColor( 1.0, 0.0, 0.0 ) );
*/
/*
* Actually draw the line "Hello World!" on to the PdfPage at
* the position 2cm,2cm from the top left corner.
* Please remember that PDF files have their origin at the
* bottom left corner. Therefore we substract the y coordinate
* from the page height.
*
* The position specifies the start of the baseline of the text.
*
* All coordinates in PoDoFo are in PDF units.
* You can also use PdfPainterMM which takes coordinates in 1/1000th mm.
*
*/
painter.SetTransformationMatrix(1,0,0,-1,0,pPage->GetPageSize().GetHeight());
painter.DrawText( 56.69, pPage->GetPageSize().GetHeight() - 56.69, "Hello World!" );
painter.DrawText( 56.69, pPage->GetPageSize().GetHeight() - 96.69, "Hello World!" );
/*
* Tell PoDoFo that the page has been drawn completely.
* This required to optimize drawing operations inside in PoDoFo
* and has to be done whenever you are done with drawing a page.
*/
painter.FinishPage();
/*
* Set some additional information on the PDF file.
*/
document.GetInfo()->SetCreator ( PdfString("examplahelloworld - A PoDoFo test application") );
document.GetInfo()->SetAuthor ( PdfString("Dominik Seichter") );
document.GetInfo()->SetTitle ( PdfString("Hello World") );
document.GetInfo()->SetSubject ( PdfString("Testing the PoDoFo PDF Library") );
document.GetInfo()->SetKeywords( PdfString("Test;PDF;Hello World;") );
/*
* The last step is to close the document.
*/
#if MEMDOCUMENT
document.Write("outputex.pdf"); //save page change
#else
document.Close();
#endif
} catch ( const PdfError & e ) {
/*
* All PoDoFo methods may throw exceptions
* make sure that painter.FinishPage() is called
* or who will get an assert in its destructor
*/
try {
painter.FinishPage();
} catch( ... ) {
/*
* Ignore errors this time
*/
}
throw e;
}
}
2 ответа
Для тех, кто изо всех сил пытается понять, почему это происходит, именно эта команда находится вверху каждой страницы (в этом примере страницы имеют размер A4), которая переворачивает содержимое вдоль оси y:
1 0 0 -1 0 841 cm
По моим наблюдениям, это очень часто встречается в PDF-файлах, созданных несколькими программами. Есть также много PDF-файлов, которые вообще не содержат этого. Я подозреваю, что это связано исключительно с фиксацией 1e07ce в Каире 1.15.4, см. https://cairographics.org/releases/ChangeLog.cairo-1.15.4.
Сложность в том, что эта команда перед любой q
(сохранить преобразование), Q
(восстановить преобразование), так что невозможно вернуться к известному преобразованию с помощью простого Q
, Другими словами, единственный способ вернуться к известному преобразованию - это проанализировать поток содержимого страницы и посмотреть, какое преобразование существует до q
/Q
пар. Затем, когда это преобразование известно, обратное преобразование может быть применено до того, как какой-либо новый контент будет наложен на существующий контент.
Разобрать страницу и получить преобразование перед любым q
:
PoDoFo::PdfPage* page = ...;
PoDoFo::PdfContentsTokenizer tokenizer(page);
const char* token = NULL;
PoDoFo::PdfVariant param;
PoDoFo::EPdfContentsType type;
std::vector<PoDoFo::PdfVariant> params;
double tf_a = 1, tf_c = 0, tf_e = 0;
double tf_b = 0, tf_d = 1, tf_f = 0;
//0 //0 //1
while(tokenizer.ReadNext(type, token, param)){
//Command
if(type == PoDoFo::ePdfContentsType_Keyword){
//First Save at page, we assume that it will eventually be paired with enough Restores to go back to the current transform
if(strcmp(token, "q") == 0)
break;
//Transform before first q, must apply the inverse when overlaying dots
else if(strcmp(token, "cm") == 0){
if(params.size() == 6){
tf_a = params[0].GetReal();
tf_b = params[1].GetReal();
tf_c = params[2].GetReal();
tf_d = params[3].GetReal();
tf_e = params[4].GetReal();
tf_f = params[5].GetReal();
invertTransform(tf_a, tf_b, tf_c, tf_d, tf_e, tf_f);
}
else
std::cout << "Warning! Found transform before first q at page with wrong number of arguments!" << std::endl;
}
else
std::cout << "Warning! Unrelated command at page before first q: " << token << std::endl;
params.clear();
}
//Parameter for command
else if(type == PoDoFo::ePdfContentsType_Variant)
params.push_back(param);
}
где invertTransform()
небольшая служебная функция:
void invertTransform(double& a, double& b, double& c, double& d, double& e, double& f){
double m_11 = a, m_12 = c, m_13 = e;
double m_21 = b, m_22 = d, m_23 = f;
//m_31 = 0.0, m_32 = 0.0, m_33 = 1.0;
double det = m_11*(/*m_33**/m_22 /*- m_32*m_23*/) - m_21*(/*m_33**/m_12/* - m_32*m_13*/) /*+ m_31*(m_23*m_12 - m_22*m_13)*/;
if(abs(det) < 1e-10){
a = 1; c = 0; e = 0;
b = 0; d = 1; f = 0;
//0 //0 //1
}
else{
double det_1 = 1.0/det;
a = det_1*( /*m_33**/m_22 /*- m_32*m_23*/); c = det_1*(-/*m_33**/m_12 /*+ m_32*m_13*/); e = det_1*( m_23*m_12 - m_22*m_13);
b = det_1*(-/*m_33**/m_21 /*+ m_31*m_23*/); d = det_1*( /*m_33**/m_11 /*- m_31*m_13*/); f = det_1*(-m_23*m_11 + m_21*m_13);
//det_1*( m_32*m_21 - m_31*m_22) det_1*(-m_32*m_11 + m_31*m_12) det_1*( m_22*m_11 - m_21*m_12)
}
}
Затем обратное преобразование (просто тождество, если не было cm
до первого q
) можно применить и вещи можно нарисовать на странице:
PoDoFo::PdfPainter painter;
painter.SetPage(page);
painter.Save();
painter.SetTransformationMatrix(tf_a, tf_b, tf_c, tf_d, tf_e, tf_f);
/* painter.Draw...() */
painter.Restore();
painter.FinishPage();
Конечно, все это решение предполагает, что может быть один cm
преобразовать и никаких других преобразований до первого q
,
Другим, гораздо более простым решением было бы поставить один q
прежде чем все в потоке и поставить один Q
после, с последующим желаемым контентом, но я не уверен, что это просто сделать с PoDoFo.
Спасибо за mkl, с помощью mkl вопрос был решен.
проблема в том, что исходный код Reflection effect.podofo имеет матрицу преобразования, которую вы можете изменить, прежде чем добавлять текст или строку в документ PDF.
добавить код, подобный этому: //
painter.SetTransformationMatrix(1,0,0,-1,0,pPage->GetPageSize().GetHeight()); // set Reflection effect
painter.Save();
painter.DrawText( 56.69, pPage->GetPageSize().GetHeight() - 56.69, "Hello World!" );
painter.DrawText( 56.69, pPage->GetPageSize().GetHeight() - 96.69, "Hello World!"