blob: d3d6fc290bc6cb899450588511e0846d828e4841 [file] [log] [blame]
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001// Copyright 2014 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Lei Zhanga6d9f0e2015-06-13 00:48:38 -07004
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07005// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
Lei Zhangb4e7f302015-11-06 15:52:32 -08007#include "public/fpdf_transformpage.h"
8
Dan Sinclair584b1e62016-03-21 09:15:45 -04009#include "core/fpdfapi/fpdf_page/include/cpdf_clippath.h"
Dan Sinclair455a4192016-03-16 09:48:56 -040010#include "core/fpdfapi/fpdf_page/include/cpdf_page.h"
Dan Sinclair584b1e62016-03-21 09:15:45 -040011#include "core/fpdfapi/fpdf_page/include/cpdf_pageobject.h"
12#include "core/fpdfapi/fpdf_page/include/cpdf_path.h"
Dan Sinclairaa403d32016-03-15 14:57:22 -040013#include "core/fpdfapi/fpdf_parser/include/cpdf_array.h"
14#include "core/fpdfapi/fpdf_parser/include/cpdf_document.h"
15#include "core/fpdfapi/fpdf_parser/include/cpdf_number.h"
16#include "core/fpdfapi/fpdf_parser/include/cpdf_reference.h"
Dan Sinclair584b1e62016-03-21 09:15:45 -040017#include "core/fpdfapi/fpdf_parser/include/cpdf_stream.h"
Lei Zhangbde53d22015-11-12 22:21:30 -080018#include "fpdfsdk/include/fsdk_define.h"
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070019
Lei Zhang6ac0d2f2015-10-02 10:08:48 -070020namespace {
21
Lei Zhang6ac0d2f2015-10-02 10:08:48 -070022void SetBoundingBox(CPDF_Page* page,
23 const CFX_ByteStringC& key,
24 float left,
25 float bottom,
26 float right,
27 float top) {
28 CPDF_Dictionary* pPageDict = page->m_pFormDict;
29 CPDF_Array* pBoundingBoxArray = new CPDF_Array;
30 pBoundingBoxArray->Add(new CPDF_Number(left));
31 pBoundingBoxArray->Add(new CPDF_Number(bottom));
32 pBoundingBoxArray->Add(new CPDF_Number(right));
33 pBoundingBoxArray->Add(new CPDF_Number(top));
34 pPageDict->SetAt(key, pBoundingBoxArray);
35}
36
37FPDF_BOOL GetBoundingBox(CPDF_Page* page,
38 const CFX_ByteStringC& key,
39 float* left,
40 float* bottom,
41 float* right,
42 float* top) {
43 CPDF_Dictionary* pPageDict = page->m_pFormDict;
Wei Li9b761132016-01-29 15:44:20 -080044 CPDF_Array* pArray = pPageDict->GetArrayBy(key);
Lei Zhang6ac0d2f2015-10-02 10:08:48 -070045 if (!pArray)
46 return FALSE;
47
Wei Li9b761132016-01-29 15:44:20 -080048 *left = pArray->GetFloatAt(0);
49 *bottom = pArray->GetFloatAt(1);
50 *right = pArray->GetFloatAt(2);
51 *top = pArray->GetFloatAt(3);
Lei Zhang6ac0d2f2015-10-02 10:08:48 -070052 return TRUE;
53}
54
55} // namespace
56
Nico Weber9d8ec5a2015-08-04 13:00:21 -070057DLLEXPORT void STDCALL FPDFPage_SetMediaBox(FPDF_PAGE page,
58 float left,
59 float bottom,
60 float right,
61 float top) {
Tom Sepezdb0be962015-10-16 14:00:21 -070062 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
Nico Weber9d8ec5a2015-08-04 13:00:21 -070063 if (!pPage)
64 return;
Lei Zhanga6d9f0e2015-06-13 00:48:38 -070065
Lei Zhang6ac0d2f2015-10-02 10:08:48 -070066 SetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070067}
68
Nico Weber9d8ec5a2015-08-04 13:00:21 -070069DLLEXPORT void STDCALL FPDFPage_SetCropBox(FPDF_PAGE page,
70 float left,
71 float bottom,
72 float right,
73 float top) {
Tom Sepezdb0be962015-10-16 14:00:21 -070074 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
Nico Weber9d8ec5a2015-08-04 13:00:21 -070075 if (!pPage)
76 return;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070077
Lei Zhang6ac0d2f2015-10-02 10:08:48 -070078 SetBoundingBox(pPage, "CropBox", left, bottom, right, top);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070079}
80
Nico Weber9d8ec5a2015-08-04 13:00:21 -070081DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetMediaBox(FPDF_PAGE page,
82 float* left,
83 float* bottom,
84 float* right,
85 float* top) {
Tom Sepezdb0be962015-10-16 14:00:21 -070086 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
Lei Zhang6ac0d2f2015-10-02 10:08:48 -070087 return pPage && GetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070088}
89
Nico Weber9d8ec5a2015-08-04 13:00:21 -070090DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetCropBox(FPDF_PAGE page,
91 float* left,
92 float* bottom,
93 float* right,
94 float* top) {
Tom Sepezdb0be962015-10-16 14:00:21 -070095 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
Lei Zhang6ac0d2f2015-10-02 10:08:48 -070096 return pPage && GetBoundingBox(pPage, "CropBox", left, bottom, right, top);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070097}
98
Nico Weber9d8ec5a2015-08-04 13:00:21 -070099DLLEXPORT FPDF_BOOL STDCALL FPDFPage_TransFormWithClip(FPDF_PAGE page,
100 FS_MATRIX* matrix,
101 FS_RECTF* clipRect) {
Tom Sepezdb0be962015-10-16 14:00:21 -0700102 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700103 if (!pPage)
104 return FALSE;
Bo Xufdc00a72014-10-28 23:03:33 -0700105
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700106 CFX_ByteTextBuf textBuf;
107 textBuf << "q ";
108 CFX_FloatRect rect(clipRect->left, clipRect->bottom, clipRect->right,
109 clipRect->top);
110 rect.Normalize();
111 CFX_ByteString bsClipping;
112 bsClipping.Format("%f %f %f %f re W* n ", rect.left, rect.bottom,
113 rect.Width(), rect.Height());
114 textBuf << bsClipping;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700115
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700116 CFX_ByteString bsMatix;
117 bsMatix.Format("%f %f %f %f %f %f cm ", matrix->a, matrix->b, matrix->c,
118 matrix->d, matrix->e, matrix->f);
119 textBuf << bsMatix;
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700120
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700121 CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400122 CPDF_Object* pContentObj =
123 pPageDic ? pPageDic->GetElement("Contents") : nullptr;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700124 if (!pContentObj)
Wei Li9b761132016-01-29 15:44:20 -0800125 pContentObj = pPageDic ? pPageDic->GetArrayBy("Contents") : nullptr;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700126 if (!pContentObj)
127 return FALSE;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700128
Tom Sepezae51c812015-08-05 12:34:06 -0700129 CPDF_Dictionary* pDic = new CPDF_Dictionary;
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400130 CPDF_Stream* pStream = new CPDF_Stream(nullptr, 0, pDic);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700131 pStream->SetData(textBuf.GetBuffer(), textBuf.GetSize(), FALSE, FALSE);
132 CPDF_Document* pDoc = pPage->m_pDocument;
133 if (!pDoc)
134 return FALSE;
135 pDoc->AddIndirectObject(pStream);
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700136
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700137 pDic = new CPDF_Dictionary;
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400138 CPDF_Stream* pEndStream = new CPDF_Stream(nullptr, 0, pDic);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700139 pEndStream->SetData((const uint8_t*)" Q", 2, FALSE, FALSE);
140 pDoc->AddIndirectObject(pEndStream);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700141
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400142 CPDF_Array* pContentArray = nullptr;
143 if (CPDF_Array* pArray = ToArray(pContentObj)) {
144 pContentArray = pArray;
Tom Sepezae51c812015-08-05 12:34:06 -0700145 CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700146 pContentArray->InsertAt(0, pRef);
147 pContentArray->AddReference(pDoc, pEndStream);
Dan Sinclairbf81c142015-10-26 16:54:39 -0400148 } else if (CPDF_Reference* pReference = ToReference(pContentObj)) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700149 CPDF_Object* pDirectObj = pReference->GetDirect();
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400150 if (pDirectObj) {
151 if (CPDF_Array* pArray = pDirectObj->AsArray()) {
152 pContentArray = pArray;
Nico Weber077f1a32015-08-06 15:08:57 -0700153 CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700154 pContentArray->InsertAt(0, pRef);
155 pContentArray->AddReference(pDoc, pEndStream);
Dan Sinclairaa435ba2015-10-22 16:45:48 -0400156 } else if (pDirectObj->IsStream()) {
Tom Sepezae51c812015-08-05 12:34:06 -0700157 pContentArray = new CPDF_Array();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700158 pContentArray->AddReference(pDoc, pStream->GetObjNum());
159 pContentArray->AddReference(pDoc, pDirectObj->GetObjNum());
160 pContentArray->AddReference(pDoc, pEndStream);
161 pPageDic->SetAtReference("Contents", pDoc,
162 pDoc->AddIndirectObject(pContentArray));
163 }
164 }
165 }
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700166
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700167 // Need to transform the patterns as well.
Wei Li9b761132016-01-29 15:44:20 -0800168 CPDF_Dictionary* pRes = pPageDic->GetDictBy("Resources");
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700169 if (pRes) {
Wei Li9b761132016-01-29 15:44:20 -0800170 CPDF_Dictionary* pPattenDict = pRes->GetDictBy("Pattern");
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700171 if (pPattenDict) {
Oliver Chang3f1c71f2016-01-11 08:45:31 -0800172 for (const auto& it : *pPattenDict) {
173 CPDF_Object* pObj = it.second;
Dan Sinclairbf81c142015-10-26 16:54:39 -0400174 if (pObj->IsReference())
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700175 pObj = pObj->GetDirect();
Dan Sinclairaa435ba2015-10-22 16:45:48 -0400176
Oliver Chang3f1c71f2016-01-11 08:45:31 -0800177 CPDF_Dictionary* pDict = nullptr;
Dan Sinclairaa435ba2015-10-22 16:45:48 -0400178 if (pObj->IsDictionary())
Dan Sinclairf1251c12015-10-20 16:24:45 -0400179 pDict = pObj->AsDictionary();
Dan Sinclairaa435ba2015-10-22 16:45:48 -0400180 else if (CPDF_Stream* pStream = pObj->AsStream())
181 pDict = pStream->GetDict();
182 else
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700183 continue;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700184
Wei Li9b761132016-01-29 15:44:20 -0800185 CFX_Matrix m = pDict->GetMatrixBy("Matrix");
Tom Sepez60d909e2015-12-10 15:34:55 -0800186 CFX_Matrix t = *(CFX_Matrix*)matrix;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700187 m.Concat(t);
Lei Zhangd983b092015-12-14 16:58:33 -0800188 pDict->SetAtMatrix("Matrix", m);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700189 }
190 }
191 }
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700192
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700193 return TRUE;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700194}
195
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700196DLLEXPORT void STDCALL
197FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,
198 double a,
199 double b,
200 double c,
201 double d,
202 double e,
203 double f) {
204 CPDF_PageObject* pPageObj = (CPDF_PageObject*)page_object;
Lei Zhang412e9082015-12-14 18:34:00 -0800205 if (!pPageObj)
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700206 return;
Tom Sepez60d909e2015-12-10 15:34:55 -0800207 CFX_Matrix matrix((FX_FLOAT)a, (FX_FLOAT)b, (FX_FLOAT)c, (FX_FLOAT)d,
208 (FX_FLOAT)e, (FX_FLOAT)f);
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700209
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700210 // Special treatment to shading object, because the ClipPath for shading
211 // object is already transformed.
Wei Li7cf13c92016-02-19 11:53:03 -0800212 if (!pPageObj->IsShading())
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700213 pPageObj->TransformClipPath(matrix);
214 pPageObj->TransformGeneralState(matrix);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700215}
216
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700217DLLEXPORT FPDF_CLIPPATH STDCALL FPDF_CreateClipPath(float left,
218 float bottom,
219 float right,
220 float top) {
Tom Sepezae51c812015-08-05 12:34:06 -0700221 CPDF_ClipPath* pNewClipPath = new CPDF_ClipPath();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700222 pNewClipPath->GetModify();
223 CPDF_Path Path;
224 Path.GetModify();
225 Path.AppendRect(left, bottom, right, top);
226 pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, FALSE);
227 return pNewClipPath;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700228}
229
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700230DLLEXPORT void STDCALL FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) {
231 delete (CPDF_ClipPath*)clipPath;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700232}
233
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700234void OutputPath(CFX_ByteTextBuf& buf, CPDF_Path path) {
235 const CFX_PathData* pPathData = path;
Lei Zhang412e9082015-12-14 18:34:00 -0800236 if (!pPathData)
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700237 return;
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700238
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700239 FX_PATHPOINT* pPoints = pPathData->GetPoints();
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700240
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700241 if (path.IsRect()) {
242 buf << (pPoints[0].m_PointX) << " " << (pPoints[0].m_PointY) << " "
243 << (pPoints[2].m_PointX - pPoints[0].m_PointX) << " "
244 << (pPoints[2].m_PointY - pPoints[0].m_PointY) << " re\n";
245 return;
246 }
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700247
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700248 CFX_ByteString temp;
249 for (int i = 0; i < pPathData->GetPointCount(); i++) {
250 buf << (pPoints[i].m_PointX) << " " << (pPoints[i].m_PointY);
251 int point_type = pPoints[i].m_Flag & FXPT_TYPE;
Dan Sinclair738b08c2016-03-01 14:45:20 -0500252 if (point_type == FXPT_MOVETO) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700253 buf << " m\n";
Dan Sinclair738b08c2016-03-01 14:45:20 -0500254 } else if (point_type == FXPT_BEZIERTO) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700255 buf << " " << (pPoints[i + 1].m_PointX) << " "
256 << (pPoints[i + 1].m_PointY) << " " << (pPoints[i + 2].m_PointX)
257 << " " << (pPoints[i + 2].m_PointY);
258 if (pPoints[i + 2].m_Flag & FXPT_CLOSEFIGURE)
259 buf << " c h\n";
260 else
261 buf << " c\n";
262 i += 2;
263 } else if (point_type == FXPT_LINETO) {
264 if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE)
265 buf << " l h\n";
266 else
267 buf << " l\n";
268 }
269 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700270}
271
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700272DLLEXPORT void STDCALL FPDFPage_InsertClipPath(FPDF_PAGE page,
273 FPDF_CLIPPATH clipPath) {
Tom Sepezdb0be962015-10-16 14:00:21 -0700274 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700275 if (!pPage)
276 return;
Lei Zhang6ac0d2f2015-10-02 10:08:48 -0700277
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700278 CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400279 CPDF_Object* pContentObj =
280 pPageDic ? pPageDic->GetElement("Contents") : nullptr;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700281 if (!pContentObj)
Wei Li9b761132016-01-29 15:44:20 -0800282 pContentObj = pPageDic ? pPageDic->GetArrayBy("Contents") : nullptr;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700283 if (!pContentObj)
284 return;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700285
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700286 CFX_ByteTextBuf strClip;
287 CPDF_ClipPath* pClipPath = (CPDF_ClipPath*)clipPath;
288 FX_DWORD i;
289 for (i = 0; i < pClipPath->GetPathCount(); i++) {
290 CPDF_Path path = pClipPath->GetPath(i);
291 int iClipType = pClipPath->GetClipType(i);
292 if (path.GetPointCount() == 0) {
293 // Empty clipping (totally clipped out)
294 strClip << "0 0 m W n ";
295 } else {
296 OutputPath(strClip, path);
297 if (iClipType == FXFILL_WINDING)
298 strClip << "W n\n";
299 else
300 strClip << "W* n\n";
301 }
302 }
Tom Sepezae51c812015-08-05 12:34:06 -0700303 CPDF_Dictionary* pDic = new CPDF_Dictionary;
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400304 CPDF_Stream* pStream = new CPDF_Stream(nullptr, 0, pDic);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700305 pStream->SetData(strClip.GetBuffer(), strClip.GetSize(), FALSE, FALSE);
306 CPDF_Document* pDoc = pPage->m_pDocument;
307 if (!pDoc)
308 return;
309 pDoc->AddIndirectObject(pStream);
Lei Zhanga6d9f0e2015-06-13 00:48:38 -0700310
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400311 CPDF_Array* pContentArray = nullptr;
312 if (CPDF_Array* pArray = ToArray(pContentObj)) {
313 pContentArray = pArray;
Tom Sepezae51c812015-08-05 12:34:06 -0700314 CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700315 pContentArray->InsertAt(0, pRef);
Dan Sinclairbf81c142015-10-26 16:54:39 -0400316 } else if (CPDF_Reference* pReference = ToReference(pContentObj)) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700317 CPDF_Object* pDirectObj = pReference->GetDirect();
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400318 if (pDirectObj) {
319 if (CPDF_Array* pArray = pDirectObj->AsArray()) {
320 pContentArray = pArray;
Nico Weber077f1a32015-08-06 15:08:57 -0700321 CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700322 pContentArray->InsertAt(0, pRef);
Dan Sinclairaa435ba2015-10-22 16:45:48 -0400323 } else if (pDirectObj->IsStream()) {
Tom Sepezae51c812015-08-05 12:34:06 -0700324 pContentArray = new CPDF_Array();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700325 pContentArray->AddReference(pDoc, pStream->GetObjNum());
326 pContentArray->AddReference(pDoc, pDirectObj->GetObjNum());
327 pPageDic->SetAtReference("Contents", pDoc,
328 pDoc->AddIndirectObject(pContentArray));
329 }
330 }
331 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700332}